.wav class source

From Jeskola Buzz Wiki
Jump to: navigation, search

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"

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;
}

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

	if (argc<=2 || *argv[1]=='?' || *(argv[1]+1)=='?')
	{
		printf("Demo application that does nothing.\n");
		printf("Usage: %s <input .wav file> <blergh>\n", argv[0]);
		return -3;
	}

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

	WaveFile* myFile = new WaveFile();

	int errorcode = myFile->OpenWav(InputFilename);

	if (errorcode==0)
	{
		myFile->PrepareToRead();
		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 (example)
			}
			sample = sample/myFile->waveHeader.numChannels;

			// ho-hum...

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

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;
	}

};