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);
}
[ back to the prod ]
