pouët.net

Bars

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//	bars - tinygraphics.cpp
//
//  Written by greystar/quantum sufficient on 12-11-2004 for the TMDC 7 compo.
//
//	The demo should fit in 4KB if proper build settings are used (see comments below).
//	The music player has real song and pattern data. I think it could be expanded a 
//	little, but I had really hoped to fit under the 3KB mark and did not continue with 
//	its development.
//
//	This code is being placed into the public domain.
//
//	Contact me on efnet irc - #c++ or #SouthCarolina .
//	http://webpages.charter.net/wyatt/
//
//	
//	Special Greets to the TMDC organizers for putting together a contest to create test 
//	code for the MS console team :)
//
//
//
//
/////////////////////////////////////////////////////////////////////////////////////////
// b u i l d  s e t t i n g s ///////////////////////////////////////////////////////////
#pragma comment(linker, "/entry:\"main\"")
#pragma comment(linker, "/opt:NOWIN98")
#pragma comment(linker, "/nodefaultlib")
#pragma comment(linker, "/merge:.data=.text")
#pragma comment(linker, "/merge:.rdata=.text")
#pragma comment(linker, "/subsystem:console")
#pragma comment(linker, "/ALIGN:512")
#pragma comment(linker, "/STACK:0x8000,0x8000")
#pragma check_stack(off)	//does not seem to work...
#pragma pack(1)


#pragma comment(lib, "kernel32")
#pragma comment(lib, "winmm")


//use the /QIfist compiler option to fix the missing __ftol symbol.
//make sure to remove /GZ from the compiler options to fix unresolved external symbol "__chkesp".
//make sure to add /Gs999999 to the compiler options to fix unresolved external symbol "__chkesp". make sure to commit the stack space.
//get floating point support from the compiler:
extern "C" { 
	int _fltused; 
}

//Can be compiled at the command line via:
//cl tinygraphics.cpp winmm.lib kernel32.lib /QIfist /Gs999999 /Os

/////////////////////////////////////////////////////////////////////////////////////////
// i n c l u d e s //////////////////////////////////////////////////////////////////////
#include<windows.h>

/////////////////////////////////////////////////////////////////////////////////////////
// c o n s t a n t s ////////////////////////////////////////////////////////////////////
const size_t xMin=0, xMax=80, yMin=0, yMax=50;
COORD size = { xMax, yMax };
typedef unsigned __int8 pixel;
pixel *frame, *pic;
HANDLE hCon;

CHAR_INFO lut[16]={
	{ 0xB0, 0x00 }, //0
	{ 0xB0, 0x08 }, //1
	{ 0xB0, 0x07 },	//2
	{ 0xB0, 0x80 },	//3
	{ 0xB0, 0x88 },	//4
	{ 0xB0, 0x0F },	//5
	{ 0xB0, 0x70 },	//6
	{ 0xB0, 0x87 },	//7
	{ 0xB1, 0x78 },	//8
	{ 0xB0, 0x78 },	//9
	{ 0xB0, 0xF0 },	//A
	{ 0xB0, 0x77 },	//B
	{ 0xB0, 0x7F },	//C
	{ 0xB0, 0xF8 },	//D
	{ 0xB0, 0xF7 },	//E
	{ 0xB0, 0xFF }	//F
};

/////////////////////////////////////////////////////////////////////////////////////////
// p r o t o t y p e s //////////////////////////////////////////////////////////////////
DWORD WINAPI PlayMusic(LPVOID);


/////////////////////////////////////////////////////////////////////////////////////////
// i m p l e m e n t a t i o n //////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
//BlitScreen() - Takes the framebuffer, converts it to a format compatible with the 
//				 console window, and then displays it in the console window.
//Precondition: frame[] must be allocated
//Postcondition:frame[] has been displayed on the console.
void BlitScreen() {
	CHAR_INFO cMap[xMax*yMax];
	pixel pix;

	//Map the frame buffer to console mode characters:
	for(size_t n=0; n<xMax*yMax; n++) {
		pix=frame[n];
		if(pix>15) pix=15;
		cMap[n].Char.AsciiChar=lut[pix].Char.AsciiChar;
		cMap[n].Attributes=lut[pix].Attributes;
	}

	//Dump the console buffer to the console window:
    COORD src={0,0};
    SMALL_RECT outrect= {0, 0, xMax-1, yMax-1};
    WriteConsoleOutput(hCon, cMap, size, src, &outrect);
}

/////////////////////////////////////////////////////////////////////////////////////////
//RotoZoom() - Rotates and zooms the bitmap in "pic[]" onto the framebuffer "frame[]".
//Precondition: Zoomfactor must not be 0.
//				frame[] and pic[] have been allocated.
//Postcondition:frame[] contains the rotated+zoomed version of pic[].
//Note: does not copy pixels with the color 0.
void RotoZoom(double theta, double zoomfactor) {
	//Get the sin, cos values from the FPU:
	double c,s;
	__asm {
		fld		theta
		fsincos
		fstp	c
		fstp	s
	}

	//Rotate+Zoom the picture:
	for(int y=yMin; y<yMax; y++) {
		for(int x=xMin; x<xMax; x++) {
			//Rotation about (0,0) is defined as: (x',y')=(x*cos(t)-y*sin(t), x*sin(t)+y*cos(t)).

			int pcx=int((x*c-y*s) * zoomfactor)%xMax;
			int pcy=int((x*s+y*c) * zoomfactor)%yMax;

			if(pcx<0)
				pcx+=80;
			
			if(pcy<0)
				pcy+=50;

			pixel pixCurrentPixel=pic[pcy*xMax + pcx];
			if(0!=pixCurrentPixel) 
				frame[y*xMax + x] = pixCurrentPixel/(zoomfactor+0.8);

		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////
//main() - performs app initialization and runs the demo graphics.
//Precondition:	none
//Postcondition:demo has been played.
//Note: [Esc] will exit the demo.
void main() {
	//Start the music thread:
	CreateThread(NULL, 0x10000, PlayMusic, NULL, 0, NULL);
	Sleep(100);	//Let audio initialization catch up before starting video.

	//Allocate buffers:
	//pixel *pic=(pixel*)GlobalAlloc(0, xMax*yMax*sizeof(pixel));
	//frame=(pixel*)GlobalAlloc(0, xMax*yMax*sizeof(pixel));
	pic=(pixel*)GlobalAlloc(GMEM_ZEROINIT, 2*xMax*yMax*sizeof(pixel));
	frame=pic+xMax*yMax;



	//Setup the console:
	hCon=GetStdHandle(STD_OUTPUT_HANDLE);
	HANDLE hStdIn=GetStdHandle(STD_INPUT_HANDLE);
    
	SetConsoleScreenBufferSize(hCon, size); //attempt to set 80x50 mode
	SetConsoleMode(hCon, 0); //disable character code processing and line wraps.

    //Make the cursor invisible:
	//CONSOLE_CURSOR_INFO cci;
    //cci.bVisible=FALSE;
    //cci.dwSize=1;   
    //SetConsoleCursorInfo(hCon, &cci);

	
	//Create a bitmap of the "copper" bars:
	for(size_t y=0; y<8; y++) {
		for(size_t x=0; x<xMax; x++) {
			pic[y*xMax+x]=15-y;
			pic[(49-y)*xMax+x]=15-y;
		}
	}




	//Main video loop:	
	double theta, zoom;
	for(;;) {
		//clear the frame buffer:
		for(int n=0; n<xMax*yMax; n++) 
			frame[n]=0;

		//Check for [Esc] key and exit if pressed.
		DWORD dw;
		if(PeekConsoleInput(hStdIn, (PINPUT_RECORD)frame, 1, &dw)) {
			if(dw>0) {
				ReadConsoleInput(hStdIn, (PINPUT_RECORD)frame, 1, &dw);
				INPUT_RECORD *p=(PINPUT_RECORD)frame;
				if( p->EventType==KEY_EVENT) {
					if(p->Event.KeyEvent.wVirtualScanCode==VK_ESCAPE) {
						break;
					}
				}
			}
		}
		
		//Draw the "copper" bars		
		RotoZoom(theta/6.0, zoom+8.0);
		RotoZoom(theta/5.0, zoom+5.0);
		RotoZoom(theta/4.0, zoom+3.0);
		RotoZoom(theta/3.0, zoom+2.0);
		RotoZoom(theta/2.0, zoom+1.0);
		RotoZoom(theta    , zoom    );

		//Blit frame buffer to console window:
		BlitScreen();
	
		//Update demo counters:	
		theta+=0.05;
		zoom-=0.0025;
		if(zoom<=0.0) 
			zoom+=1.0;

		//Frame rate limiting:
		Sleep(10);		
	}
	
	BlitScreen();
	char credits[]="bars by greystar/quantum.sufficient";
	WriteFile(hCon, credits, sizeof(credits), NULL, NULL);
	ExitProcess(0);
}


/////////////////////////////////////////////////////////////////////////////////////////
//PlayMusic() - plays the demo soundtrack
//Precondition:	none.
//Postcondition:does not exit.
//Note: this function must be run in its own thread.
DWORD WINAPI PlayMusic(LPVOID) {

	size_t n=0;
	//	 0    1     2    3   4     5   6     7    8   9    10   11 
	//	 A    B-    B    C   C#    D   E-    E	  F	  F#    G   G#
	unsigned __int8 freqtbl[12]={
		 32,  34,  36,  38,  40,  42,  44,  48,  50,  54,  56,  60
	};

	typedef unsigned __int8 PatternData;
	const size_t VOLUME_LEVEL=0x20;
	const size_t NUM_CHANNELS=2;
	const size_t PATTERN_LENGTH=16;
	size_t nSamplePos[NUM_CHANNELS];

	//Songs are composed of a list of orders. Each order points to a pattern and has a note transposition value.
	struct OrderData {
		PatternData *pattern;	//note data, arranged in two tracks of PATTERN_LENGTH notes.
		__int8 transposition;	//amount to transpose each note in the pattern.
	};


	PatternData pattern0[NUM_CHANNELS*PATTERN_LENGTH]= {
		12, 0,12, 0,12, 0,12, 0,12, 0,12, 0,12, 0,12, 0,	//track 1 (left channel)
		12, 0,24, 0,24, 0,12, 0,24, 0,24, 0,12, 0,24, 0		//track 2 (right channel)
	};
/*	PatternData pattern1[NUM_CHANNELS*PATTERN_LENGTH]= {
		24,24,24,24, 0, 0, 0, 0, 0, 0, 0, 0,22,22,22,22,	//track 1 (left channel)
		12, 0,24, 0,24, 0,12, 0,24, 0,24, 0,12, 0,24, 0		//track 2 (right channel)
	};
	PatternData pattern2[NUM_CHANNELS*PATTERN_LENGTH]= {
		24,24,24,24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	//track 1 (left channel)
		12, 0,24, 0,24, 0,12, 0,24, 0,24, 0,12, 0,24, 0		//track 2 (right channel)
	};
  */


	const size_t SONG_LENGTH=9;
	OrderData song[SONG_LENGTH] = { //song data is { pattern, transposition }, { pattern, transposition }, ...
		{ pattern0, 5 },
		{ pattern0, 5 },
		{ pattern0, 7 },
		{ pattern0, 7 },
		{ pattern0, 3 },
		{ pattern0, 6 },
		{ pattern0, 5 },
		{ pattern0, 5 },

		{ pattern0, 5 },


/*		{ pattern1, 17 },
		{ pattern1, 17 },
		{ pattern1, 18 },
		{ pattern1, 12 },
		{ pattern1, 17 },
		{ pattern1, 17 },
		{ pattern2, 17 },
		{ pattern0, 5 },
*/
  
	};


	//Allocate the sound structures:
	HWAVEOUT hWave;
	const size_t AUDIO_BUF_SIZE=22050*2;
	//char *audio=(char*)GlobalAlloc(GMEM_ZEROINIT, AUDIO_BUF_SIZE);
	//WAVEFORMATEX *pwfx=(WAVEFORMATEX*)GlobalAlloc(GMEM_ZEROINIT, sizeof(WAVEFORMATEX));
	//WAVEHDR *pwh=(WAVEHDR*)GlobalAlloc(GMEM_ZEROINIT, sizeof(WAVEHDR));
	char *audio=(char*)GlobalAlloc(GMEM_ZEROINIT, AUDIO_BUF_SIZE+sizeof(WAVEFORMATEX)+sizeof(WAVEHDR));
	WAVEFORMATEX *pwfx=(WAVEFORMATEX*)(audio+AUDIO_BUF_SIZE);
	WAVEHDR *pwh=(WAVEHDR*)(audio+AUDIO_BUF_SIZE+sizeof(WAVEFORMATEX));

	
	//Set up 8-bit stereo PCM audio at 11025 Hz:
	pwfx->wFormatTag=WAVE_FORMAT_PCM;
	pwfx->nChannels=2;
	pwfx->nSamplesPerSec=11025;
	pwfx->wBitsPerSample=8;
	pwfx->nBlockAlign=pwfx->nChannels*pwfx->wBitsPerSample/8;
	pwfx->nAvgBytesPerSec=pwfx->nSamplesPerSec*pwfx->nBlockAlign;
	pwfx->cbSize=0; //amount of *extra* data past end of structure.

	//Set up a looping buffer to send to the wave mapper:
	pwh->lpData=audio;
	pwh->dwBufferLength=AUDIO_BUF_SIZE;
	pwh->dwUser=NULL;
	pwh->dwFlags=WHDR_BEGINLOOP | WHDR_ENDLOOP;
	pwh->dwLoops=-1;
   	
	//Open the sound hardware through the wave mapper:
	if(!waveOutOpen(&hWave, WAVE_MAPPER, pwfx, NULL, NULL, CALLBACK_NULL)) {
		if(!waveOutPrepareHeader(hWave, pwh, sizeof(WAVEHDR))) {
			if(!waveOutWrite(hWave, pwh, sizeof(WAVEHDR))) {
				size_t songpos=0, patternpos, channel=0;
				//Loop through the song and play the note data:
				for(songpos=0; songpos<SONG_LENGTH; songpos++) {
					for(patternpos=0; patternpos<PATTERN_LENGTH; patternpos++) {
						for(n=0; n<AUDIO_BUF_SIZE/2; n++) {
							//Mix the left and right channels (tracks):
							//left channel:
							if( ((nSamplePos[0]>>8)%8192)>4096)
								audio[(n<<1)]=0x80+VOLUME_LEVEL;
							else
								audio[(n<<1)]=0x80-VOLUME_LEVEL;

							//right channel:
							if( ((nSamplePos[1]>>8)%8192)>4096)
								audio[(n<<1)+1]=0x80+VOLUME_LEVEL;
							else
								audio[(n<<1)+1]=0x80-VOLUME_LEVEL;

							//Update sample positions:							
							for(channel=0; channel<NUM_CHANNELS; channel++) {
								size_t nNote=song[songpos].pattern[channel*PATTERN_LENGTH+patternpos];
								if(nNote>0) { //Only update the sample position if the note number is not 0 (0==note cutoff)
									nNote+=song[songpos].transposition;
									nSamplePos[channel]+=((nNote/12+1)*freqtbl[nNote%12])<<7; //scaling factor for sample
								}
							}
						}					
						Sleep(80); //Wait until time to play next note.
					}
					//Make the song loop at the end:
					if(songpos==SONG_LENGTH-1)
						songpos=0;
				}
			}
		}

		//Silence and close the wave mapper:
		//waveOutReset(hWave);
		//waveOutClose(hWave);
	}
	return(0);
}