Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Nov 4th, 2004, 2:45 PM   #1
BruceLeroy
Newbie
 
Join Date: Nov 2004
Posts: 18
Rep Power: 0 BruceLeroy is on a distinguished road
I wanted to make a simple app that draws that waveform of a wavefile. I actually made a lot of progress in one day and got it to work. But then I thought about restructuring my code a little bit and ran into all manner of problems.

The biggest problem I'm having now is understanding how my app draws a meaningful wave even when the array from which it draws its values has each of its elements set to 0. There is no point in the entire app when I am filling this array with values, and yet when I launch the program I'm staggered to find a well-formed wave.

Its a bit of an elaborate problem so please bear with me as I try to explain...

I'm going to dump my code here because I have no idea where the problem may lie

/*

Questions go here:

1. is a reference another word for handle?
2. research TCHAR datatype.
3. Difference between WNDCLASSEX and WNDCLASS?
  WNDCLASSEX is the newer one. There are more differences but for our purposes
  lets assume that the newer one is the "better one". 
*/

//always necessary to include windows.h for making windowed programs


#include <windows.h>
#include "stdafx.h"

#include <SP.h>;

//function prototypes

short int* extractChannelValues(short int* PCM_DATA, long arraySize, int speaker);

short int* calculatePoints(short int* speakerData, long arraySize);

void drawAxis();

void draw(short int* graphPoints, int Resolution, int speaker);

short int modulus(short int value);

ATOM produceAndregisterClass();

int createAndShowWindow();

LRESULT CALLBACK WindowProcedure
  (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam);

//global variables follow

HINSTANCE hinst; //a reference to the application instance

HWND mainWindow; //this reference will eventually point to the main Window,
   //once instantiated

TCHAR className[] = "myWindow"; //the name of the "class" which we will register

short int* PCM_Data;
long sampleValues = 0;

short int* LEFT_SPEAKER;
short int* LEFT_SPEAKER_GRAPH_POINTS;

/* Main Steps 

1. Define a window "class". Populate a WND Class Ex structure.
2. Register the window class. Pass the WNDCLASSEX struct pointer to RegisterClassEx.
3. Provide a windows procedure. This is the function which will deal with the user's
  interaction with the window.
4. Create and show the window.
5. Put application into a "message loop". 

*/

//entry point into a windowed application is main 

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

	
	/*load wave file into memory*/
	WAVEDATA drawData = validWAVEFile("x3.wav");

	PCM_Data = drawData.PCM_Data;
	sampleValues = drawData.dataSize;

	/*extract the information for left channel*/
	LEFT_SPEAKER = extractChannelValues(PCM_Data, sampleValues, 0);

	LEFT_SPEAKER_GRAPH_POINTS = calculatePoints(LEFT_SPEAKER, sampleValues/2);
	
	hinst = hInstance;

	if ( produceAndregisterClass() == 0 )
 return -1; //producing and registering class did not work for whatever reason
    //thus we will exit early.

	if ( createAndShowWindow () == 0 )
 return -1; //again, something went wrong so exit early.

	ShowWindow(mainWindow,nCmdShow);
	UpdateWindow(mainWindow);

	//message loop goes here

	BOOL bRet;

	MSG msg;

	while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
	{ 
 if (bRet == -1)
 {
    return -1;
 }
 else
 {	
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
 }
 
	}
	
	return 0;
}

ATOM produceAndregisterClass()
{

	//first let us populate a WNDCLASSEX struct. 
	//this struct contains all the details of windows we will display

	WNDCLASSEX windowClass = { 0 };

	//set the size. when dealing with WNDCLASSEX structures, the RHS of the
	//code is always the same when setting the cbSize member

	windowClass.cbSize = sizeof(WNDCLASSEX); 

	//a style helps determine the window's behavior. these are enumerated
	//at the MSDN website.
	//it is my understanding that using the | operator allows you to specify a combination of styles
	//I've set it to redraw the window whenever the height or width is adjusted.
	
	windowClass.style = CS_HREDRAW | CS_VREDRAW;

	//pointer to the programmer defined window procedure to deal with the user interaction with window

	windowClass.lpfnWndProc = (WNDPROC) WindowProcedure; 

	//number of extra bytes to allocate to structure. i'm saying 0 because this is a simple window,
	//although it remains to be seen exactly how you work how much more you need if the window was
	//"complex".

	windowClass.cbClsExtra = 0;

	windowClass.cbWndExtra = 0;

	//assign the reference to the application instance

	windowClass.hInstance = hinst;

	//assignment of an icon. NULL means system provides default.

	windowClass.hIcon = 0;

	//assignment of cursor. NULL means default.

	windowClass.hCursor = 0;

	//background colour assignment, this can be a handle to a data type called HBRUSH or
	//alternatively it can be a specified colour (if the latter must cast to HBRUSH).
	//always must add 1 to chosen colour.

	windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

	//this should be a string pointing to a resource that specifies a menu. 
	//but a NULL value means no menu, which is what we want for this simple window.

	windowClass.lpszMenuName = 0;

	//assign the class name (which we've stored in a global variable)

	windowClass.lpszClassName = className;

  //i guess this icon member is for the icon your app should have when it is listed
	//in a directory. don't really know but its not that important. we'll just use NULL.

	windowClass.hIconSm = 0; 

	//The structure is filled and now we must register it.

	return RegisterClassEx(&windowClass);
}

//call this function to create and display window only after registering the class.

int createAndShowWindow()
{

	
mainWindow = CreateWindow( className,
            0, 
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            0, 
            CW_USEDEFAULT, 
            0, 
            0, 
            0, 
            hinst, 
            0);
   
/*

mainWindow = CreateWindowEx(
   	WS_EX_APPWINDOW,//window behavior
   	className, 
   	"Test Window", //title bar string
   	WS_CAPTION, //style
   	CW_USEDEFAULT, //x position
   	CW_USEDEFAULT, //y position
   	CW_USEDEFAULT, //width
   	CW_USEDEFAULT, //height
   	0, //handle to parent window, but there is no parent so...?
   	0, //menu handle. null means use the one specified in class
   	hinst, //handle instance
   	0 //lparam value...not really sure what its for
   	);
*/

if ( !mainWindow ) return 0;

return 1;

}

//this function always has this signature and it deals with the user interaction
//messages are passed from the operating system to this function which processes 
//the messages

LRESULT CALLBACK WindowProcedure
  (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam)

{

//the window will do nothing, except vanish when we ask it to
//if there is a message we have not dealt with in the case statement
//we use the defaultwindowprocedure called DefWindowPoc.

	switch (message)
  {
//	PAINTSTRUCT ps; 
  //HDC hdc; 
 
 case WM_PAINT: 
 	drawAxis();
 	/*get the co-ordinates for the left channel graph sub graph*/
 	draw(LEFT_SPEAKER_GRAPH_POINTS, 1024, 0);
 	return 0;
      /* hdc = BeginPaint(hwnd, &ps); 
      TextOut(hdc, 0, 0, "Hello, Windows!", 15); 
      EndPaint(hwnd, &ps); 
      return 0L;*? 
 	*/
    case WM_DESTROY:
 	delete (LEFT_SPEAKER_GRAPH_POINTS);
 	PostQuitMessage (0);
      return 0;

  }
  return DefWindowProc (hwnd, message, wParam, lParam );

}

void draw(short int* graphPoints, int Resolution, int speaker)
{

	int silentVoltage = 0;

	if (speaker == 0) silentVoltage = 180; else silentVoltage=540;

	int x = 0;

	HDC hdc; 

	hdc = GetDC(mainWindow);

	MoveToEx(hdc, x, silentVoltage, (LPPOINT) NULL);

	int sample = 0;

	while ( x < Resolution )
	{
 sample = graphPoints[x];
 sample=sample/182;
 if (sample > 0) sample = silentVoltage - sample;
 if (sample < 0) sample = modulus(sample) + silentVoltage;
 if (sample == 0) sample = silentVoltage;

 LineTo(hdc, x, sample);

 x++;

	}

	ReleaseDC(mainWindow, hdc);

}
	
void drawAxis()
{
	HDC hdc; 

	hdc = GetDC(mainWindow);
	
	/* POLYbezier function for one curve needs
	1 point for starting point
	2 point for control point
	3 point for control point
	4 point for where we are going to
	
	POINT points[] = { 100,508, 125, 75, 150, 125, 175, 100};
	//MoveToEx(hdc, 500, 500, (LPPOINT) NULL);
	PolyBezier(hdc, points, 4);
	*/
	
	//draw line dividing left speaker and right speaker
	int x = 0;
	int y = 360;

  MoveToEx(hdc, (int) x, (int) y, (LPPOINT) NULL); 
  LineTo(hdc, (int) x + 1023, (int) y); 

	//draw 0 volt line for left speaker
	x = 0;
	y = 180;
	MoveToEx(hdc, x, y, (LPPOINT) NULL);
	LineTo(hdc, (int) x + 1023, (int) y); 

	//draw 0 volt line for right speaker

	x = 0;
	y = 540;
	MoveToEx(hdc, x, y, (LPPOINT) NULL);
  LineTo(hdc, (int) x + 1023, (int) y); 

	//move the pen back to the start of the left speaker

	x=0;
	y=180;
	MoveToEx(hdc, x, y, (LPPOINT) NULL);

	ReleaseDC(mainWindow, hdc);
}

short int* calculatePoints(short int* speakerData, long arraySize)
{

	//how many values to plot along x-axis

	int horizontalResolution = 1024; 

	//declare return value array

	short int* GraphPoints;

	GraphPoints = new short int[horizontalResolution];

	//calculate how many samples each pixel is representative of

	int bufferSize = arraySize / horizontalResolution;

	if ( bufferSize % 2 ) bufferSize++;

	short int* buffer;

	buffer = new short int[bufferSize];

	short int sample = 0; //the value to plot at the i'th iteration

	long i = 0; //the loop counter

	int x = 0; //the pixel for the current iteration

return GraphPoints;

}

short int* extractChannelValues(short int* PCM_DATA, long arraySize, int speaker)
{

	long i = 0;

	long speakerValues = arraySize / 2;

	short int* channelValues;

	channelValues = new short int[speakerValues];

	if ( speaker == 0 ) i=0; else i=1;

	long x=0;

	while ( x < speakerValues )
	{
 channelValues[x]=PCM_Data[i];

 i= i+2;
 x++;
	}

return channelValues;

}

short int modulus(short int value)
{
	if (value < 0) return value*-1;
	else return value;
}

don't worry, i'm not asking you to read all of it - i'll fill you in on them most salient details.

first i read in a wave file called x.wav. By read in I mean I store all the amplitude information in the wavefile into a short int array. In the code this array is the global variable PCM_Data.

next step: i declare another short int array called LEFT_SPEAKER. Every odd value in the PCM_Data array corresponds to sounds coming out of the left speaker, so I copy all of them into the array called LEFT_SPEAKER.

Now here's the puzzling bit. I have yet another array called LEFT_SPEAKER_GRAPH_POINTS which is to be populated with data that I will use to draw the waveform for the leftspeaker. This is exactly 1024 elements across because I am assuming a horizontal resolution of 1024. I use the function called calculatePoints() to populate LEFT_SPEAKER_GRAPH_POINTS with the necessary values. How I did this previously was to do some basic processing on the values of the LEFT_SPEAKER array. However since my new code was messing up on me I cleaned out the calculatePoints() function so that it is effectively returning an empty array. And yet when I launch the program, I am greeted with this waveform:




i just don't understand how on earth it can draw such a good-looking waveform when i haven't even supplied it with the data?? And if you're curious, no it looks nothing like the "true" waveform for the file x.wav.

If anyone could shed any light on this topic, please please let me know. I have spent 2 days wondering why on earth my program is behaving so strangely. it almost seems to defy logic!
BruceLeroy is offline   Reply With Quote
Old Nov 4th, 2004, 10:23 PM   #2
kurifu
Expert Programmer
 
kurifu's Avatar
 
Join Date: Jul 2004
Location: Halifax, Nova Scotia (Canada)
Posts: 784
Rep Power: 5 kurifu is on a distinguished road
Send a message via ICQ to kurifu Send a message via MSN to kurifu
Oooooooooooooooookkkkkkk... this is going to be a bit of speculation, but should give you an idea as to where to start assessing this problem.

One of the most interesting aspects about memory allocation in C/C++ is that memory will not intialize when it is allocated until you initialize it manually. So what happens is that if you allocate memory on the heap or the stack unless you specify an initial vlue, or any value trying to read from the variable will likely succeed, and the value be true... just somewhat "random"

Random as in it will read back the previous contents of that memory location. When memory is "deleted" it is not actually cleared, the memory location is just marked as available, which as you can see creates this problem.

My guess is that somewhere in there you are not initialize an variable/array, or are setting a pointer to point to an uninitialized chunk of memory, maybe even unallocated (usually this throws an Access Violation however).

An interesting thing to note in the image you provided is that while the waveform seems a bit repetitive, it is not perfectly such, which is also very plausible given the circumstances I explained above.

The best way to fix this error is to likely set a breakpoint and check the datablock which represents the waveform, or where the waveform should be, figure out what is writing to this datablock, if the datablock is ever even being initialize to 0, or NULL, and if something could be writing to the datablock when it shouldn't be.

Hopefully this helps some...
__________________
Clifford Matthew Roche &lt;geek@cliffordroche.com&gt;
Web Hosting: http://www.crd-hosting.com
Consulting: http://www.crdev-consulting.com
kurifu is offline   Reply With Quote
Old Nov 5th, 2004, 5:19 AM   #3
BruceLeroy
Newbie
 
Join Date: Nov 2004
Posts: 18
Rep Power: 0 BruceLeroy is on a distinguished road
thanks, yeah I think you are right. It must have something to do with old data being thrown back onto the screen.

So if I manually make sure I muck around with the contents of the array, I shouldn't get this waveform appearing. well thats the theory anyway. I'll let you know how I get on.

thanks for the info though - this language has a thousand and one quirks I have to get used to. I've been raised on Java and that kind of holds your hand and never lets anything go wrong. C++ seems a lot more down and dirty and DIY.
BruceLeroy is offline   Reply With Quote
Old Nov 5th, 2004, 6:40 AM   #4
BruceLeroy
Newbie
 
Join Date: Nov 2004
Posts: 18
Rep Power: 0 BruceLeroy is on a distinguished road
OK here's the thing....

in my message loop, if the WM_PAINT message is recieved I call the draw function.

The draw function refers to a global array called LEFT_SPEAKER_GRAPH_POINTS (which has already been populated with data by calculatePoints() ). The draw function uses the lineto function to draw the data. However no matter what it always draws a nothing to the screen, almost as if the array is empty (even though I have now processed it in such a way as to make sure the array has got data in it).

BUT! If in the draw function, I load up the wavefile in there and use that resulting array to draw to the screen, it works! But I don't want it to work this way because it means having to constantly load the file back into memory per every reception of the WM_PAINT message. The whole idea of restructuring my code was to analyse the wavefile first, get all the graph points out, save those to a global array and plot that whenever the draw function is called. But like I said, that approach results in an empty screen :huh:

Is there a specific way of sending arrays between functions - it almost as if their content is spilling out in transit!

So here's the new way, which doesn't work:

/*global variables*/
short int* PCM_Data; //data from the wavefile, for both speakers
long sampleValues = 0; //number of entries to PCM_Data

short int* LEFT_SPEAKER; //all the values from PCM_Data that are meant for the left speaker
short int* LEFT_SPEAKER_GRAPH_POINTS; //the y-co ordinates for the graph

...

//in the main function

/*load wave file into memory*/
WAVEDATA drawData = validWAVEFile("1000.wav");

PCM_Data = drawData.PCM_Data;
sampleValues = drawData.dataSize;

/*extract the information for left channel*/
LEFT_SPEAKER = extractChannelValues(PCM_Data, sampleValues, 0);

LEFT_SPEAKER_GRAPH_POINTS = calculatePoints(LEFT_SPEAKER, sampleValues/2);

.... //some code to build and insantiate the window

//in the window procedure function

switch (message)
  {
            case WM_PAINT: 
 	drawAxis();
 	/*get the co-ordinates for the left channel graph sub graph*/
 	draw(LEFT_SPEAKER_GRAPH_POINTS, 1024, 0);
 	return 0;
   ....
   }

//now the code for the calculatePoints function
short int* calculatePoints(short int* speakerData, long arraySize)
{

	//how many values to plot along x-axis

	int horizontalResolution = 1024; 

	//declare return value array

	short int* GraphPoints;

	GraphPoints = new short int[horizontalResolution];

	//calculate how many samples each pixel is representative of

	int bufferSize = arraySize / horizontalResolution;

	if ( bufferSize % 2 ) bufferSize++;

	short int* buffer;

	buffer = new short int[bufferSize];

	short int sample = 0; //the value to plot at the i'th iteration

	long i = 0; //current index of the sample array

	int x = 0; //the pixel for the current iteration

	double RMS = 0; //variable for ROOT MEANS SQUARE calculation

	while ( true )
	{	
 
 for (int j=i; j<bufferSize; j++)
 {
 	/*square and sum the values*/
 	RMS+= (speakerData[j+i])*(speakerData[j+i]);
 }
 
 RMS = RMS/bufferSize;

 RMS = sqrt(RMS);
 
        GraphPoints[x]=(short int) RMS;

 i=2;

 x++;

 if (x==1024) break;
	}
}

and finally the draw function

void draw(short int* graphPoints, int Resolution, int speaker)
{

	int silentVoltage = 0;

	if (speaker == 0) silentVoltage = 180; else silentVoltage=540;

	int x = 0;

	HDC hdc; 

	hdc = GetDC(mainWindow);

	MoveToEx(hdc, x, silentVoltage, (LPPOINT) NULL);

	long i=0;
	short int sample = 0;

	while ( x < Resolution )
	{
 sample = graphPoints[x];
 sample=sample/180;
 if (sample > 0) sample = silentVoltage - sample;
 if (sample < 0) sample = modulus(sample) + silentVoltage;
 if (sample == 0) sample = silentVoltage;

 LineTo(hdc, x, (int) sample);

 x++;

 i=i+100;
	}

	ReleaseDC(mainWindow, hdc);

}

now contrast all that to if i just load up the wavefile from within the draw function and start to randomnly draw values from it onto the screen (THIS method works, but why doesn't the other?)

void draw(short int* graphPoints, int Resolution, int speaker)
{

	WAVEDATA wave = validWAVEFile("x3.wav"); //load up wave in draw function

	int silentVoltage = 0;

	if (speaker == 0) silentVoltage = 180; else silentVoltage=540;

	int x = 0;

	HDC hdc; 

	hdc = GetDC(mainWindow);

	MoveToEx(hdc, x, silentVoltage, (LPPOINT) NULL);

	long i=0;
	short int sample = 0;

	while ( x < Resolution )
	{
 sample=wave.PCM_Data[i];
 sample=sample/180;
 if (sample > 0) sample = silentVoltage - sample;
 if (sample < 0) sample = modulus(sample) + silentVoltage;
 if (sample == 0) sample = silentVoltage;

 LineTo(hdc, x, (int) sample);

 x++;

 i=i+2;
	}

	ReleaseDC(mainWindow, hdc);

}
BruceLeroy is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 2:15 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC