Difference between revisions of ".wav class source"

From Jeskola Buzz Wiki
Jump to: navigation, search
(Original attempt to write this. Improvements are welcome.)
(No difference)

Revision as of 22:55, 23 November 2010

WaveLoaderClass.cpp

An attempt to write a C++ version of a .wav class.

// WaveLoaderClass.cpp: Defines the entry point for the console application.

// #include "StdAfx.h"
// #include "direct.h"
#include "WaveLoader.h"

double const pi=3.1415926535897932384626433832795;

char* DisplayChunkName(unsigned int *value)
{
	unsigned char *copyfrom;
	copyfrom = (unsigned char*)value;
	static char txt[5];
	txt[0] = *copyfrom++;
	txt[1] = *copyfrom++;
	txt[2] = *copyfrom++;
	txt[3] = *copyfrom++;
	txt[4] = 0;
	return txt;
}

// stereo split filter by Joachim
class LinkwitzRiley
{
public:
	LinkwitzRiley()
	{
		this->LR4_Reset();
	}

	// Linkwitz-Riley-4 reset all variables
	void LR4_Reset()
	{
		lr4_wc = 0;
		lr4_wc2 = 0;
		lr4_wc3 = 0;
		lr4_wc4 = 0;
		lr4_k = 0;
		lr4_k2 = 0;
		lr4_k3 = 0;
		lr4_k4 = 0;
		lr4_sqrt2 = 0;
		lr4_sq_tmp1 = 0;
		lr4_sq_tmp2 = 0;
		lr4_a_tmp = 0;
		lr4_b1 = 0;
		lr4_b2 = 0;
		lr4_b3 = 0;
		lr4_b4 = 0;
		for (int ch=0; ch<4; ch++)
		{
			lr4_xm4lp[ch] = 0;
			lr4_xm3lp[ch] = 0;
			lr4_xm2lp[ch] = 0;
			lr4_xm1lp[ch] = 0;
			lr4_ym4lp[ch] = 0;
			lr4_ym3lp[ch] = 0;
			lr4_ym2lp[ch] = 0;
			lr4_ym1lp[ch] = 0;
			lr4_a0lp[ch] = 0;
			lr4_a1lp[ch] = 0;
			lr4_a2lp[ch] = 0;
			lr4_a3lp[ch] = 0;
			lr4_a4lp[ch] = 0;
		}
		return;
	}

	// Linkwitz-Riley-4 calculate coefficients
	void LR4_Calc_Coefficients( double fc, double srate )
	{
		lr4_wc = 2*pi*fc;
		lr4_wc2 = lr4_wc*lr4_wc;
		lr4_wc3 = lr4_wc2*lr4_wc;
		lr4_wc4 = lr4_wc2*lr4_wc2;
		lr4_k = lr4_wc/tan(pi*fc/srate);
		lr4_k2 = lr4_k*lr4_k;
		lr4_k3 = lr4_k2*lr4_k;
		lr4_k4 = lr4_k2*lr4_k2;
		lr4_sqrt2 = sqrtf(2);
		lr4_sq_tmp1 = lr4_sqrt2*lr4_wc3*lr4_k;
		lr4_sq_tmp2 = lr4_sqrt2*lr4_wc*lr4_k3;
		lr4_a_tmp = 4*lr4_wc2*lr4_k2+2*lr4_sq_tmp1+lr4_k4+2*lr4_sq_tmp2+lr4_wc4;

		lr4_b1 = (4*(lr4_wc4+lr4_sq_tmp1-lr4_k4-lr4_sq_tmp2))/lr4_a_tmp;
		lr4_b2 = (6*lr4_wc4-8*lr4_wc2*lr4_k2+6*lr4_k4)/lr4_a_tmp;
		lr4_b3 = (4*(lr4_wc4-lr4_sq_tmp1+lr4_sq_tmp2-lr4_k4))/lr4_a_tmp;
		lr4_b4 = (lr4_k4-2*lr4_sq_tmp1+lr4_wc4-2*lr4_sq_tmp2+4*lr4_wc2*lr4_k2)/lr4_a_tmp;

		// lowpass
		lr4_a0lp[0] = lr4_wc4/lr4_a_tmp;		// left
		lr4_a1lp[0] = 4*lr4_wc4/lr4_a_tmp;
		lr4_a2lp[0] = 6*lr4_wc4/lr4_a_tmp;
		lr4_a3lp[0] = lr4_a1lp[0];
		lr4_a4lp[0] = lr4_a0lp[0];
		lr4_a0lp[1] = lr4_wc4/lr4_a_tmp;		// right
		lr4_a1lp[1] = 4*lr4_wc4/lr4_a_tmp;
		lr4_a2lp[1] = 6*lr4_wc4/lr4_a_tmp;
		lr4_a3lp[1] = lr4_a1lp[0];
		lr4_a4lp[1] = lr4_a0lp[1];

		// highpass
		lr4_a0lp[2] = lr4_k4/lr4_a_tmp;			// left
		lr4_a1lp[2] = -4*lr4_k4/lr4_a_tmp;
		lr4_a2lp[2] = 6*lr4_k4/lr4_a_tmp;
		lr4_a3lp[2] = lr4_a1lp[2];
		lr4_a4lp[2] = lr4_a0lp[2];
		lr4_a0lp[3] = lr4_k4/lr4_a_tmp;			// right
		lr4_a1lp[3] = -4*lr4_k4/lr4_a_tmp;
		lr4_a2lp[3] = 6*lr4_k4/lr4_a_tmp;
		lr4_a3lp[3] = lr4_a1lp[3];
		lr4_a4lp[3] = lr4_a0lp[3];
	}

	// Linkwitz-Riley-4 in-loop filtering, use ch 0,1 for lowpass left,right, 2,3 is highpass
	double LR4_Process( double input, int ch )
	{
		double tempy = lr4_a0lp[ch]*input+lr4_a1lp[ch]*lr4_xm1lp[ch]+lr4_a2lp[ch]*lr4_xm2lp[ch]+lr4_a3lp[ch]*lr4_xm3lp[ch]+lr4_a4lp[ch]*lr4_xm4lp[ch]-lr4_b1*lr4_ym1lp[ch]-lr4_b2*lr4_ym2lp[ch]-lr4_b3*lr4_ym3lp[ch]-lr4_b4*lr4_ym4lp[ch];
		lr4_xm4lp[ch] = lr4_xm3lp[ch];
		lr4_xm3lp[ch] = lr4_xm2lp[ch];
		lr4_xm2lp[ch] = lr4_xm1lp[ch];
		lr4_xm1lp[ch] = input;
		lr4_ym4lp[ch] = lr4_ym3lp[ch];
		lr4_ym3lp[ch] = lr4_ym2lp[ch];
		lr4_ym2lp[ch] = lr4_ym1lp[ch];
		lr4_ym1lp[ch] = tempy;
		return tempy;
	}

private:
	double lr4_wc;
	double lr4_wc2;
	double lr4_wc3;
	double lr4_wc4;
	double lr4_k;
	double lr4_k2;
	double lr4_k3;
	double lr4_k4;
	double lr4_sqrt2;
	double lr4_sq_tmp1;
	double lr4_sq_tmp2;
	double lr4_a_tmp;
	double lr4_a0lp[4];
	double lr4_a1lp[4];
	double lr4_a2lp[4];
	double lr4_a3lp[4];
	double lr4_a4lp[4];
	double lr4_b1;
	double lr4_b2;
	double lr4_b3;
	double lr4_b4;
	double lr4_xm4lp[4];
	double lr4_xm3lp[4];
	double lr4_xm2lp[4];
	double lr4_xm1lp[4];
	double lr4_ym4lp[4];
	double lr4_ym3lp[4];
	double lr4_ym2lp[4];
	double lr4_ym1lp[4];
};


int main(int argc, char* argv[])
{

	if (argc<=2 || *argv[1]=='?' || *(argv[1]+1)=='?')
	{
		printf(".Wav to sound level calculator by Joachim Michaelis 2010\n");
		printf("Usage: %s <input .wav file> <number of output values>\n", argv[0]);
		return -3;
	}

	char *InputFilename = argv[1];
	char *PixelCountArg = argv[2];

	WaveFile* myFile = new WaveFile();
	LinkwitzRiley* myFilter = new LinkwitzRiley();

	unsigned long PixelCount = 0;
	PixelCount = atoi(PixelCountArg);
	if (PixelCount==0 || PixelCount>10000)
	{
		printf("Illegal number of output values\n");
		return -3;
	}
	int errorcode = myFile->OpenWav(InputFilename);

	if (errorcode==0)
	{
		// unsigned long int total_length = 10;		// number of output values (pixels in visualization)

		myFile->PrepareToRead();
		if (myFile->number_of_samples==0 || myFile->waveHeader.sampleRate==0)
		{
			printf("File contains %d samples (%d Hz)\n", myFile->number_of_samples, myFile->waveHeader.sampleRate);
		} else
		{
			// prepare the split filter and set its frequency
			myFilter->LR4_Calc_Coefficients(400, myFile->waveHeader.sampleRate);

			unsigned long samples_per_pixel = myFile->number_of_samples / myFile->waveHeader.numChannels / PixelCount;
			unsigned long pixel = 0;
			unsigned long s2 = 0;
			float summed_bass = 0;
			float summed_treb = 0;
			float max_bass = 0;
			float max_treb = 0;
			printf("Peak bass, Average bass, Peak treble, Average treble\n");
			for (unsigned long s=0; s<myFile->number_of_samples; s+=myFile->waveHeader.numChannels)
			{
				float sample = 0;
				for(int channel=0; channel<myFile->waveHeader.numChannels; channel++)
				{
					sample += myFile->Read1MonoSample();		// add all channels together to mono
				}
				sample = sample/myFile->waveHeader.numChannels;

				float bass = fabsf((float)myFilter->LR4_Process((double)sample, 0));		// 0=left bass
				float treb = fabsf((float)myFilter->LR4_Process((double)sample, 2));		// 2=left treble (we only use mono here)

				summed_bass += bass*bass;
				summed_treb += treb*treb;
				if (bass>max_bass)  max_bass = bass;
				if (treb>max_treb)  max_treb = treb;
				if (s2>=samples_per_pixel)
				{
					pixel++;
					s2 = 0;
					summed_bass = sqrtf(summed_bass/(float)samples_per_pixel);
					summed_treb = sqrtf(summed_treb/(float)samples_per_pixel);
					printf("%.6f, %.6f, %.6f, %.6f\n", max_bass, summed_bass, max_treb, summed_treb);
					summed_bass = 0;
					summed_treb = 0;
					max_bass = 0;
					max_treb = 0;
				}
				s2++;

			}
		}
		myFile->CloseWav();
	} else
	{
		printf("Error %d loading .wav file.\n", errorcode);
	}












/*
	if (argc<=2 || *argv[1]=='?' || *(argv[1]+1)=='?')
	{
		printf(".Filter test by Joachim Michaelis 2010\n");
		printf("Usage: %s <input .wav file> <output raw pcm file>\n", argv[0]);
		return -3;
	}

	char *InputFilename = argv[1];
	char *OutputFilename = argv[2];

	LinkwitzRiley* myFilter = new LinkwitzRiley();
	myFilter->LR4_Reset();

	WaveFile* myFile = new WaveFile();
	int errorcode = myFile->OpenWav(InputFilename);
	// static char here[300]; getcwd(here, _MAX_PATH);		// current directory

	if (errorcode==0)
	{
		printf("RIFF file type  : %s\n", DisplayChunkName(&myFile->fileType));
		printf("RIFF file length: %d\n", myFile->fileLength);
		myFile->ReadAllIntoFloat();
		myFile->CloseWav();

		if (myFile->waveHeader.sampleRate!=0)
		{
			myFilter->LR4_Calc_Coefficients(400, myFile->waveHeader.sampleRate);
		} else
		{
			myFilter->LR4_Calc_Coefficients(400, 44100);
		}

		float ileft = 0;
		float iright = 0;
		float oleft = 0;
		float oright = 0;
		float ilefto = 0;
		float irighto = 0;

		printf("Processing audio... \n");

		// process stuff
		for (unsigned int counter=0; counter<myFile->audiodata_count-1; counter+=myFile->waveHeader.numChannels)
		{
			ilefto = ileft;
			irighto = iright;
			ileft = myFile->audiodata[counter];
			iright = myFile->audiodata[counter+1];

			// oleft = oleft*0.99f + atanf((ileft-ilefto)*200)/150+0.04f;
			// oright = oright*0.99f + atanf((iright-irighto)*200)/150+0.04f;
			// myFile->audiodata[counter+1] = sinf(oright*3)+0.536f;
			// myFile->audiodata[counter] = sinf(oleft*3)+0.536f;

			float leftbass = (float)myFilter->LR4_Process((double)ileft, 0);
			float rightbass = (float)myFilter->LR4_Process((double)iright, 1);
			float lefttreble = (float)myFilter->LR4_Process((double)ileft, 2);
			float righttreble = (float)myFilter->LR4_Process((double)ileft, 3);
			myFile->audiodata[counter+1] = lefttreble;
			myFile->audiodata[counter] = righttreble;
		}

		myFile->WriteWav(OutputFilename);
		myFile->FreeMem();
	} else
	{
		printf("Error code: %d\n", errorcode);
	}
*/
	// printf("Press enter to continue\n");
	// static char dummy[10];
	// scanf("hmm", dummy);
	return 0;
}

WaveLoader.h

// Wave Loader Class by Joachip

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define ReadAndConvertToFloat 0
#define ReadSampleBySample 1

struct WaveHeaderStructure
{
	unsigned short compressionCode;
	unsigned short numChannels;
	unsigned int sampleRate;
	unsigned int bytesPerSecond;
	unsigned short blockAlign;
	unsigned short bitsPerSample;
	unsigned short extraFormatBytes;
};

class WaveFile
{
private:
	FILE* fp;
	FILE* fpw;
	unsigned long pointer;
	unsigned long data_chunk_fileposition;
	unsigned int chunkHeader;			// char[4]
	unsigned int chunkLength;

public:
	WaveHeaderStructure waveHeader;
	unsigned int identifier;			// char[4]
	unsigned int fileLength;
	unsigned int fileType;				// char[4]
	float* audiodata;					// for stereo left and right are interleaved
	unsigned long number_of_samples;	// count to this when using Read1MonoSample() and such
	unsigned int audiodata_count;

	WaveFile()
	{
		this->Initialize();
	}

	void Initialize()
	{
		fp = 0;
		fpw = 0;
		pointer = 0;
		data_chunk_fileposition = 0;
		fileLength = 0;
		waveHeader.sampleRate = 0;
		audiodata = NULL;
		audiodata_count = 0;
		number_of_samples = 0;
	}

	int OpenWav(char *filename)
	{
		if((fp = fopen(filename, "rb"))==NULL)
		{
			printf("Cannot open input file \"%s\".\n", filename);
			fp = 0;
			return 1;
		}

		identifier = GetChunkName();
		fileLength = GetLength_le();
		fileType = GetChunkName();
		if (identifier!=0x46464952)	return 2;		// not a RIFF file (warning: this check is not endianness aware)
		if (fileType!=0x45564157)	return 3;		// not a WAVE file
		return 0;
	}

	void ReadAllIntoFloat()
	{
		this->Parse(ReadAndConvertToFloat);
	}

	// place pointer at start of the data chunk and get ready to call Read1MonoSample() a lot of times
	void PrepareToRead()
	{
		this->Parse(ReadSampleBySample);
		fseek(fp, data_chunk_fileposition, SEEK_SET);
	}

	float Read1MonoSample()
	{
		if (waveHeader.numChannels==0)  return 0;
		float value = 0;
		for (int currentChannel=0; currentChannel<waveHeader.numChannels; currentChannel++)
		{
			if (waveHeader.bitsPerSample==8)
			{
				value += ((float)( fgetc(fp) & 0xFF) )/170;
			}

			if (waveHeader.bitsPerSample==16)
			{
				value += ((float)( (fgetc(fp) & 0xFF) | ((signed char)fgetc(fp))<<8 ) )/32768;
			}

			if (waveHeader.bitsPerSample==24)
			{
				value += ((float)( (fgetc(fp) & 0xFF) | ((signed char)fgetc(fp))<<8 | ((signed char)fgetc(fp))<<16 ) )/8388608;
			}
		}
		value = value/waveHeader.numChannels;
		return value;
	}

	void CloseWav()
	{
		if (fp!=0)
		{
			fclose(fp);
			fp = 0;
		}

		if (fpw!=0)
		{
			fclose(fpw);
			fpw = 0;
		}
	}

	void FreeMem()
	{
		if (audiodata!=NULL)
		{
			delete[] audiodata;
			audiodata = NULL;
		}
	}

	int WriteWav(char *filename)
	{
		if (waveHeader.sampleRate==0 || audiodata==NULL)
		{
			printf("No wave data to write.\n", filename);
			return 5;
		}

		if((fpw = fopen(filename, "wb"))==NULL)
		{
			printf("Cannot open output file \"%s\".\n", filename);
			fpw = 0;
			return 4;
		}

		signed int value = 0;
		unsigned int counter;
		static char byte1 = 0;
		static char byte2 = 0;
		static char byte3 = 0;

		if (waveHeader.bitsPerSample==8)
		{
			for (counter=0; counter<audiodata_count; counter++)
			{
				value = (int)(audiodata[counter]*128);
				if (value>127)  value = 127;
				if (value<-128)  value = -128;
				byte1 = (unsigned char)(value & 0xFF);
				fputc(byte1, fpw);
			}
		}

		if (waveHeader.bitsPerSample==16)
		{
			for (counter=0; counter<audiodata_count; counter++)
			{
				// audiodata[counter] = sinf((float)counter/40.0f)*0.6f;
				value = (int)(audiodata[counter]*32768);
				if (value>32767)  value = 32767;
				if (value<-32768)  value = -32768;
				byte1 = value & 0xFF;
				byte2 = (value>>8) & 0xFF;
				fputc(byte1, fpw);
				fputc(byte2, fpw);
			}
		}

		if (waveHeader.bitsPerSample==24)
		{
			for (counter=0; counter<audiodata_count; counter++)
			{
				value = (int)(audiodata[counter]*8388608);
				if (value>8388607)  value = 8388607;
				if (value<-8388608)  value = -8388608;
				byte1 = value & 0xFF;
				byte2 = (value>>8) & 0xFF;
				byte2 = (value>>16) & 0xFF;
				fputs(&byte1, fpw);
				fputs(&byte2, fpw);
				fputs(&byte3, fpw);
			}
		}

		fclose(fpw);
		fpw = 0;
		return 0;
	}

private:

	void Parse(int readmode)
	{
		if (fp==0)  return;

		// get length
		fseek(fp, 0, SEEK_END);
		unsigned long ActualFileSize = ftell(fp);

		// set at first chunk
		pointer = 12;
		data_chunk_fileposition = 0;
		chunkLength = 4;
		while (pointer>8 && pointer<ActualFileSize && chunkLength!=0)
		{
			// seek to next chunk
			fseek(fp, pointer, SEEK_SET);
			chunkHeader = GetChunkName();
			chunkLength = GetLength_le();
			// printf("Noget: %d, %08X, chunklength=%X (filesize=%X)\n", pointer, chunkHeader, chunkLength, ActualFileSize);
			if (chunkLength>4 && (pointer+chunkLength)<ActualFileSize)
			{
				// printf("\nChunk type found: %s (%d bytes)\n", DispChunkName(&chunkHeader), chunkLength);

				// fmt  chunk
				if (chunkHeader==' tmf' || chunkHeader=='fmt ')
				{
					// printf("Processing format chunk...\n");
					waveHeader.compressionCode = Get16ubit_le();
					waveHeader.numChannels = Get16ubit_le();
					waveHeader.sampleRate = GetLength_le();
					waveHeader.bytesPerSecond = GetLength_le();
					waveHeader.blockAlign = Get16ubit_le();
					waveHeader.bitsPerSample = Get16ubit_le();
					// waveHeader.extraFormatBytes = Get16ubit_le();		// returns wrong result!?
					// printf("numChannels=%d, sampleRate=%d, bitsPerSample=%d\n", waveHeader.numChannels, waveHeader.sampleRate, waveHeader.bitsPerSample);
				}

				// data chunk (but only if we already have the fmt chunk)
				if ((chunkHeader=='atad' || chunkHeader=='data') && waveHeader.sampleRate>0)
				{
					if (readmode==ReadAndConvertToFloat)
					{
						DataChunkToFloat();
					}

					if (readmode==ReadSampleBySample)
					{
						number_of_samples = chunkLength/(waveHeader.bitsPerSample/8)/waveHeader.numChannels;
						data_chunk_fileposition = pointer+8;
					}
				}

				// ToDo: remember unknown chunks

			} else
			{
				// printf("RIFF ended\n");
			}
			pointer += chunkLength+8;
		}
	}

	void DataChunkToFloat()
	{
		// printf("Processing data chunk...\n");

		// malloc and convert to floats
		audiodata = new float[chunkLength/(waveHeader.bitsPerSample/8)];
		unsigned long sample_skip_bytes = waveHeader.bitsPerSample/8; //  * waveHeader.numChannels;
		unsigned int counter=0;
		float value = 0;
		signed int temp = 0;

		if (waveHeader.bitsPerSample==8)
		{
			for (unsigned long datapointer=0; datapointer<chunkLength; datapointer += sample_skip_bytes)
			{
				value = ((float)( fgetc(fp) & 0xFF) )/128;
				audiodata[counter++] = value;
			}
		}

		if (waveHeader.bitsPerSample==16)
		{
			for (unsigned long datapointer=0; datapointer<chunkLength; datapointer += sample_skip_bytes)
			{
				temp = (fgetc(fp) & 0xFF) | (((signed char)fgetc(fp))<<8);
				// value = sinf((float)datapointer/70.0f)*0.6f;
				audiodata[counter++] = ((float)temp) / 32768;
			}
		}

		if (waveHeader.bitsPerSample==24)
		{
			for (unsigned long datapointer=0; datapointer<chunkLength; datapointer += sample_skip_bytes)
			{
				value = ((float)( (fgetc(fp) & 0xFF) | ((signed char)fgetc(fp))<<8 | ((signed char)fgetc(fp))<<16 ) )/8388608;
				audiodata[counter++] = value;
			}
		}
		audiodata_count = counter;
	}

	void GetChunkName2(char *destination)
	{
		for(int a=0; a<4; a++)
		{
			int i = fgetc(fp);
			if(i == EOF) break;
			*destination++ = (char)i;
		}
	}

	char* DispChunkName(unsigned int *value)
	{
		unsigned char *copyfrom;
		copyfrom = (unsigned char*)value;
		static char txt[5];
		txt[0] = *copyfrom++;
		txt[1] = *copyfrom++;
		txt[2] = *copyfrom++;
		txt[3] = *copyfrom++;
		txt[4] = 0;
		return txt;
	}


	signed short Get16sbit_le()
	{
		// get a 16 bit word in little endian (intel byte order)
		signed short value = fgetc(fp) | (fgetc(fp)<<8);
		return value;
	}

	unsigned short Get16ubit_le()
	{
		// get a 16 bit word in little endian (intel byte order)
		unsigned short value = fgetc(fp) | (fgetc(fp)<<8);
		return value;
	}

	unsigned int GetLength_le()
	{
		// get a 32 bit length in little endian
		unsigned int value = fgetc(fp) | (fgetc(fp)<<8) | (fgetc(fp)<<16) | (fgetc(fp)<<24);
		return value;
	}

	// using an int because char[4] and char act weird
	unsigned int GetChunkName()
	{
		// get a 32 bit length in little endian
		unsigned int value = fgetc(fp) | (fgetc(fp)<<8) | (fgetc(fp)<<16) | (fgetc(fp)<<24);
		return value;
	}

};