/* Analyzer.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 "Analyzer.h"
#include "MainWindowStuff.h"
#include "AnalyzerSpec.h"
#include "Memory.h"
#include "InteractionWindow.h"
#include "DataMunging.h"
#include "Numbers.h"


struct AnalyzerRec
	{
		MainWindowRec*			MainWindow;
		char*								AnalyzerString;
		MyBoolean						StereoFlag;
		float								InverseVolume;

		largefixedsigned		LeftMinimum;
		largefixedsigned		LeftMaximum;
		largefixedsigned		RightMinimum; /* unused for mono */
		largefixedsigned		RightMaximum; /* unused for mono */
		long								FrameCount;

		AnalyzerRec*				Next;
	};


static AnalyzerRec*				FreeList = NIL;


/* flush free list elements */
void							FlushCachedAnalyzerStuff(void)
	{
		while (FreeList != NIL)
			{
				AnalyzerRec*		Temp;

				Temp = FreeList;
				FreeList = FreeList->Next;
				ReleasePtr((char*)Temp);
			}
	}


/* create a new analyzer processor */
AnalyzerRec*			NewAnalyzer(struct MainWindowRec* MainWindow,
										struct AnalyzerSpecRec* Template, MyBoolean StereoFlag,
										float InverseVolume)
	{
		AnalyzerRec*		Analyzer;

		CheckPtrExistence(MainWindow);
		CheckPtrExistence(Template);
		if (FreeList != NIL)
			{
				Analyzer = FreeList;
				FreeList = FreeList->Next;
			}
		 else
			{
				Analyzer = (AnalyzerRec*)AllocPtrCanFail(sizeof(AnalyzerRec),"AnalyzerRec");
				if (Analyzer == NIL)
					{
					 FailurePoint1:
						return NIL;
					}
			}
		Analyzer->AnalyzerString = CopyPtr(GetAnalyzerSpecString(Template));
		if (Analyzer->AnalyzerString == NIL)
			{
			 FailurePoint2:
				Analyzer->Next = FreeList;
				FreeList = Analyzer;
				goto FailurePoint1;
			}
		SetTag(Analyzer->AnalyzerString,"Analyzer->AnalyzerString");
		Analyzer->MainWindow = MainWindow;
		Analyzer->StereoFlag = StereoFlag;
		Analyzer->InverseVolume = InverseVolume;
		Analyzer->LeftMinimum = (signed long)0x7fffffff;
		Analyzer->LeftMaximum = -(signed long)0x7fffffff;
		Analyzer->RightMinimum = (signed long)0x7fffffff;
		Analyzer->RightMaximum = -(signed long)0x7fffffff;
		Analyzer->FrameCount = 0;
		return Analyzer;
	}


/* dispose of the analyzer; write data to window */
void							DisposeAnalyzer(AnalyzerRec* Analyzer)
	{
		InteractionWindowRec*	Interaction;
		char*									String;

		CheckPtrExistence(Analyzer);
		Interaction = MainWindowGetInteractionWindow(Analyzer->MainWindow);
		if (Interaction == NIL)
			{
				return;
			}
		InteractionWindowAppendString(Interaction,
			SYSTEMLINEFEED"Results for analyzer \x22",
			StrLen(SYSTEMLINEFEED"Results for analyzer \x22"));
		InteractionWindowAppendString(Interaction,Analyzer->AnalyzerString,
			PtrSize(Analyzer->AnalyzerString));
		InteractionWindowAppendString(Interaction,
			"\x22:"SYSTEMLINEFEED"  Frames Processed:         ",
			StrLen("\x22:"SYSTEMLINEFEED"  Frames Processed:         "));
		String = IntegerToString(Analyzer->FrameCount);
		if (String != NIL)
			{
				InteractionWindowAppendString(Interaction,String,PtrSize(String));
				ReleasePtr((char*)String);
			}
		if (Analyzer->FrameCount != 0)
			{
				if (Analyzer->StereoFlag)
					{
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Left Channel Minimum:     ",
							StrLen(SYSTEMLINEFEED"  Left Channel Minimum:     "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->LeftMinimum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Left Channel Maximum:     ",
							StrLen(SYSTEMLINEFEED"  Left Channel Maximum:     "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->LeftMaximum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Right Channel Minimum:    ",
							StrLen(SYSTEMLINEFEED"  Right Channel Minimum:    "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->RightMinimum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Right Channel Maximum:    ",
							StrLen(SYSTEMLINEFEED"  Right Channel Maximum:    "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->RightMaximum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
					}
				 else
					{
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Minimum:                  ",
							StrLen(SYSTEMLINEFEED"  Minimum:                  "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->LeftMinimum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
						InteractionWindowAppendString(Interaction,
							SYSTEMLINEFEED"  Maximum:                  ",
							StrLen(SYSTEMLINEFEED"  Maximum:                  "));
						String = LongDoubleToString(Analyzer->InverseVolume
							* largefixed2double(Analyzer->LeftMaximum),10,1e-4,1e6);
						if (String != NIL)
							{
								InteractionWindowAppendString(Interaction,String,PtrSize(String));
								ReleasePtr((char*)String);
							}
					}
			}
		InteractionWindowAppendString(Interaction,
			SYSTEMLINEFEED SYSTEMLINEFEED,StrLen(SYSTEMLINEFEED SYSTEMLINEFEED));
		ReleasePtr(Analyzer->AnalyzerString);
		Analyzer->Next = FreeList;
		FreeList = Analyzer;
	}


/* apply analyzer to some stuff */
void							ApplyAnalyzer(largefixedsigned* Data, long NumFrames,
										AnalyzerRec* Analyzer)
	{
		long						Scan;

		CheckPtrExistence(Analyzer);
		CheckPtrExistence(Data);
		Analyzer->FrameCount += NumFrames;
		if (Analyzer->StereoFlag)
			{
				for (Scan = 0; Scan < NumFrames; Scan += 1)
					{
						PRNGCHK(Data,&(Data[2 * Scan]),sizeof(Data[2 * Scan]));
						if (Data[2 * Scan] < Analyzer->LeftMinimum)
							{
								Analyzer->LeftMinimum = Data[2 * Scan];
							}
						if (Data[2 * Scan] > Analyzer->LeftMaximum)
							{
								Analyzer->LeftMaximum = Data[2 * Scan];
							}
						PRNGCHK(Data,&(Data[2 * Scan + 1]),sizeof(Data[2 * Scan + 1]));
						if (Data[2 * Scan + 1] < Analyzer->RightMinimum)
							{
								Analyzer->RightMinimum = Data[2 * Scan + 1];
							}
						if (Data[2 * Scan + 1] > Analyzer->RightMaximum)
							{
								Analyzer->RightMaximum = Data[2 * Scan + 1];
							}
					}
			}
		 else
			{
				for (Scan = 0; Scan < NumFrames; Scan += 1)
					{
						PRNGCHK(Data,&(Data[Scan]),sizeof(Data[Scan]));
						if (Data[Scan] < Analyzer->LeftMinimum)
							{
								Analyzer->LeftMinimum = Data[Scan];
							}
						if (Data[Scan] > Analyzer->LeftMaximum)
							{
								Analyzer->LeftMaximum = Data[Scan];
							}
					}
			}
	}
