Tag Archives: sdl 1.2

Does SDL provide 64 bit compatibility?

Yes, SDL is 64 bit compatible.

If you experience troubles: First of all, make sure the problem is really caused by the 64 bit operating system you use. Does the program run without any problems if run it in a 32 bit operating system?

Make sure, that the Free Pascal compiler you use is the 64 bit version. There are different links on the download page of Free Pascal to different 64 bit operating systems for different architectures (Intel, AMD, PowerPC), so did you install the right 64 bit version of FPC?

If all this applies and you still get an error saying

sdlutils.pas Warning: Conversion between ordinals and pointers is not portable

and/or

sdlutils.pas Error: Typecast has different size (4 -> 8) in assignment

(or similar messages) then your problem clearly is related to 64 bit compatibility.

In the FPC reference is written about this type-checking error:

If you typecast a pointer to an ordinal type of a different size (or vice-versa), this can cause problems. This is a warning to help in finding the 32-bit specific code where cardinal/longint is used to typecast pointers to ordinals. A solution is to use the ptrint/ptruint types instead.

That is exactly what is done by sdlutils.pas and many other files and causes the errors and warnings!

In other words, the size of pointers and integers is different on 64 bit systems. They should be of same size to make conversion safe. To circumvent this problem you have to replace a conversion of UINT32(POINTER) by PTRUINT(POINTER) and INTEGER(POINTER) by PTRINT(POINTER). The PTRUINT and PTRINT integer types are always the same size as the pointer by definition. For FPC users both types are introduced since version 1.9.3. For other compilers (e.g. Delphi, Kylix, GPC, …) they are not and you have to define them yourself (see below).

Examples:

1) sdlutils.pas:

p := Pointer( Uint32( SrcSurface.pixels ) + UInt32( y ) * SrcSurface.pitch + UInt32( x ) * bpp );" gets "p := Pointer( PtrUInt( SrcSurface.pixels ) + UInt32( y ) * SrcSurface.pitch + UInt32( x ) * bpp );

2) sdlutils.pas:

Addr1 := cardinal( Pixels );" gets "Addr1 := PtrUInt( Pixels );

3) sdl_flic.pas:

line := PUint8(Integer(line) + flic.Surface.pitch);" gets "line := PUint8(PtrInt(line) + flic.Surface.pitch);

Definition of PTRUINT and PTRINT for non-FPC compilers:

{$ifndef FPC}

type

PtrInt = LongInt;

PtrUInt = LongWord;

{$endif}

There is a patch which is replacing all the questionable parts of the JEDI-SDL package, so you wouldn’t have to do it manually. Furthermore it is introducing the definition of the new types for non-FPC compilers. Link: Patch for 64 bit support. Unfortunately it is only useful if you manage your JEDI-SDL files by CVS or SVN. Nevertheless you could download the patch and check what the patch is changing and how to do it properly (the examples are generated from the patch).

More infos about this topic in the forum: Forum discussion about 64 bit compatibility. Thanks to Cybermonkey for his helpful response.

What is SDL and SDL2?

SDL is the abbreviation of Simple DirectMedia Layer.

Originally when refering to SDL, SDL 1.2 was meant. It is the predecessor of modern SDL 2.0 (sometimes SDL2). Nowadays, when refering to SDL, it depends on context if you really mean the old SDL 1.2 or the modern SDL 2.0.

The  obsolete SDL 1.2 and the modern SDL 2.0 are a set of units which provide a free, easy and platform-independent access to features needed for developing high performance games and applications. This includes easy access to graphic, sound and input handling (keyboard, mouse, joystick) for Free Pascal and other Pascal dialects.

Who made SDL and SDL2?

SDL was developed between 1998 and 2001 by Sam Lantinga, the chief programmer of the software company Loki Games. In 2002 the company got bankrupt, but Lantinga went on developing SDL. So it got updated continuously  until today.

In August 2013 the successor SDL 2.0 has been released. SDL 2.0 introduces a lot of new features which allow development of high performance applications using up-to-date technologies.

Although the original library isn’t written in Pascal, fortunately the SDL 2.0 headers got translated to Pascal by Tim Blume and others, so the SDL 2.0 library is usable for Pascal developers as well.

What is this page about?

This page is made to help you to start with the SDL and/or SDL2 (Simple Directmedia Layer) library under Free Pascal (or other Pascal dialects) and to acquaint yourself with SDL’s concepts and commands.

Be aware though that my tutorials gives just a brief overview and introduction to the SDL and SDL2 library and are far from being all-embracing.

The tutorials aim at Pascal programmers knowing the basic concepts (loops, functions, pointers) of Pascal and now like to progress to SDL and/or SDL2.

Chapter 8a: Converting SDL image to OpenGL texture (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

It is highly recommended that you read the previous Chapter 8. The code from last chapter was used and modified to show how the conversion works. However, I won’t explain twice everything already introduced in Chapter 8. Also I’d like to express here that NeHe Productions’ OpenGL tutorial 06 “Texture Mapping” and the translated (by Dominique Louis) Jedi-SDL file was inspiring me a lot for this chapter.

You need this software:

Software Version Source Description
OpenGL driver Usually your graphic card provides the corresponding OpenGL driver and you don’t have to do anything. And if so it is very likely that version 1.1 is fully supported. However if you are one of the few poor people whose graphic card doesn’t support OpenGL, check the graphic card’s manufacturer’s homepage for OpenGL drivers.

Now following the whole code at once as usual. As you will notice many lines are exactly the same as in Chapter 8.

PROGRAM chap8a;
USES CRT, SDL, GL, GLU;

VAR
userkey:CHAR;
screen, picture:pSDL_SURFACE;
h,hh,th,thh:REAL;
ogl_texture:pGLUINT;

BEGIN

//some calculations needed for a regular tetrahedron with side length of 1
h:=SQRT(0.75); //height of equilateral triangle
hh:=h/2;       //half height of equilateral triangle
th:=0.75;      //height of tetrahedron
thh:=th/2;     //half height of tetrahedron

SDL_INIT(SDL_INIT_VIDEO);

SDL_GL_SETATTRIBUTE(SDL_GL_RED_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SETATTRIBUTE(SDL_GL_DOUBLEBUFFER, 1);

screen:=SDL_SETVIDEOMODE(640, 480, 0, SDL_OPENGL);
IF screen=NIL THEN HALT;

//preparing SDL image
picture:=SDL_LOADBMP('C:\fpsdl256.bmp');
IF picture=NIL THEN HALT;

//preparing OpenGL texture
NEW(ogl_texture);
glGENTEXTURES(1, ogl_texture);
glBINDTEXTURE(GL_TEXTURE_2D, ogl_texture^);
glTEXIMAGE2D(GL_TEXTURE_2D, 0, 3, picture^.w, picture^.h, 0,
             GL_RGB, GL_UNSIGNED_BYTE, picture^.pixels);
glTEXPARAMETERi(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTEXPARAMETERi(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SDL_FREESURFACE(picture);


glCLEARCOLOR(0.0, 0.0, 1.0, 0.0);
glVIEWPORT(0,0,640,480);
glMATRIXMODE(GL_PROJECTION);
glLOADIDENTITY;
gluPERSPECTIVE(45.0, 640.0/480.0, 1.0, 3.0);
glMATRIXMODE(GL_MODELVIEW);
glLOADIDENTITY;
glCLEAR(GL_COLOR_BUFFER_BIT);
glENABLE(GL_CULL_FACE);
glTRANSLATEf(0.0, 0.0, -2.0);

REPEAT
SDL_DELAY(50);

glROTATEf(5, 0.0, 1.0, 0.0);
glCLEAR(GL_COLOR_BUFFER_BIT);

//drawing textured face of tetrahedron
glENABLE(GL_TEXTURE_2D);
glBEGIN(GL_TRIANGLES);
    glTEXCOORD2f(2,2);
    glVERTEX3f(thh, 0.0, 0.0);
    glTEXCOORD2f(0,0);
    glVERTEX3f(-thh, hh, 0.0);
    glTEXCOORD2f(0,2);
    glVERTEX3f(-thh, -hh, 0.5);
glEND;
glDISABLE(GL_TEXTURE_2D);

//drawing remaining three untextured faces
glBEGIN(GL_TRIANGLES);
    glCOLOR3f(0.0, 1.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
    glVERTEX3f(-thh, hh, 0.0);

    glCOLOR3f(1.0, 0.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, -hh, -0.5);

    glCOLOR3f(1.0, 1.0, 1.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
glEND;

SDL_GL_SWAPBUFFERS;
UNTIL keypressed;

glDELETETEXTURES(1, ogl_texture);
DISPOSE(ogl_texture);
SDL_QUIT;
END.

This code will again draw a tetrahedron which is spinning, as known from Chapter 8. However, this time one face is textured with the “Free Pascal meets SDL” image known from Chapter 3. Now lets go through the code step by step.

PROGRAM chap8a;
USES CRT, SDL, GL, GLU;

VAR
userkey:CHAR;
screen, picture:pSDL_SURFACE;
h,hh,th,thh:REAL;
ogl_texture:pGLUINT;

The program is called “chap8a”. Additionally to the variables defined in the previous chapter there are two new variables. The SDL surface “picture” which will store the SDL image before converting it to an OpenGL texture. ogl_texture is an integer pointer variable (provided by the OpenGL Uitility Library (GLU), so pGLUINT) which is needed to reference to the OpenGL texture we will create from the SDL image.

BEGIN

//some calculations needed for a regular tetrahedron with side length of 1
h:=SQRT(0.75); //height of equilateral triangle
hh:=h/2;       //half height of equilateral triangle
th:=0.75;      //height of tetrahedron
thh:=th/2;     //half height of tetrahedron

SDL_INIT(SDL_INIT_VIDEO);

SDL_GL_SETATTRIBUTE(SDL_GL_RED_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SETATTRIBUTE(SDL_GL_DOUBLEBUFFER, 1);

screen:=SDL_SETVIDEOMODE(640, 480, 0, SDL_OPENGL);
IF screen=NIL THEN HALT;

The code shown here is discussed in detail in Chapter 8. In short the tetrahedron parameters are calculated, some important OpenGL scene settings are applied and finally the SDL video subsystem is intilized.

//preparing SDL image
picture:=SDL_LOADBMP('C:\fpsdl256.bmp');
IF picture=NIL THEN HALT;

First we should load a simple BMP image to a SDL surface as known from Chapter 3. There are some limitations about the height and length of images if used as OpenGL textures. Their pixel height and pixel length has to be power of 2. So whatever image you use, its height and lengths should fulfill the following equation: f(n) = 2n. So appropriate values are: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,… The height and length don’t have to be of the same size, so an image with height 64 px and width 32 px is perfectly acceptable. This means the image of Chapter 3 with height and length of 200 x 200 px is not acceptable. A new image with 256 x 256 dimensions is therefore provided now:

Free Pascal meets SDL images with 256x256 dimensions
Free Pascal meets SDL images with 256×256 dimensions
//preparing OpenGL texture
NEW(ogl_texture);
glGENTEXTURES(1, ogl_texture);
glBINDTEXTURE(GL_TEXTURE_2D, ogl_texture^);
glTEXIMAGE2D(GL_TEXTURE_2D, 0, 3, picture^.w, picture^.h, 0,
             GL_RGB, GL_UNSIGNED_BYTE, picture^.pixels);
glTEXPARAMETERi(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTEXPARAMETERi(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SDL_FREESURFACE(picture);

First the pointer “ogl_texture” gets some space. glGENTEXTURES(number of IDs, array of integer pointer) generates one or more OGL integer identifiers for textures. Anyway, we just have one texture, so we just need one texture identifier, therefore we request “1” and ogl_texture should point at it. If we need to identify the texture we just need to call ogl_texture from now on.

glBINDTEXTURE(target, texture) essentially creates a texture object of type: GL_TEXTURE_1D or GL_TEXTURE_2D. Usually textures in 2d and 3d games are two-dimensional, so GL_TEXTURE_2D is a good choice. Now it is clear, ogl_texture will be a 2d texture.

Briefly, glTEXIMAGE2D(target, mipmap level, internal image format, width, height, border, pixel format, pixel type, actual pixel data) creates the actual 2d texture. The target is GL_TEXTURE_2D again since we are looking for creating a 2d texture. The mipmap level should be set to 0 because we wouldn’t want to have a mipmap effect here. A higher number corresponds to the number’s mipmap level, anyway in the example for a number different from 0 there is no image at all finally. The internal image format is RGB because the image is a RGB image, anyway there is a large list of possibilities for this parameter, you should look it up in the internet if you’re interested. The width and height of the image in pixels is received from the SDL image. The border is off (values 0 and 1 are acceptable). The pixel format is RGB, too, so again SDL_RGB is the right choice here. The pixel explains how the pixel data is stored. The pixel data from the SDL image is stored as unsigned byte (GL_UNSIGNED_BYTE). Finally the pixel data pointer of the SDL image is needed. Essentially the SDL image is now transformed to an OGL texture!

Briefly, glTEXPARAMETERi(target, texture parameter, parameter value) allocates a certain value to a specific texture parameter. The possible parameters and values are OGL specific and won’t be treated here in more detail. Anyway, again we are concerned about our 2d texture, so the target is GL_TEXTURE_2D. The parameters to be set are GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER. They are used to define how to treat textures that have to be drawn to a smaller or larger scale. The routine used for this is specified by GL_LINEAR.

Since the SDL image isn’t needed anymore it can be omitted as known by SDL_FREESURFACE.

glCLEARCOLOR(0.0, 0.0, 1.0, 0.0);
glVIEWPORT(0,0,640,480);
glMATRIXMODE(GL_PROJECTION);
glLOADIDENTITY;
gluPERSPECTIVE(45.0, 640.0/480.0, 1.0, 3.0);
glMATRIXMODE(GL_MODELVIEW);
glLOADIDENTITY;
glCLEAR(GL_COLOR_BUFFER_BIT);
glENABLE(GL_CULL_FACE);
glTRANSLATEf(0.0, 0.0, -2.0);

This part is completely described in Chapter 8. Nothing has changed for this part. In short, the viewport is set up so that the tetrahedron finally can be seen.

REPEAT
SDL_DELAY(50);

glROTATEf(5, 0.0, 1.0, 0.0);
glCLEAR(GL_COLOR_BUFFER_BIT);

//drawing textured face of tetrahedron
glENABLE(GL_TEXTURE_2D);
glBEGIN(GL_TRIANGLES);
    glTEXCOORD2f(2,2);
    glVERTEX3f(thh, 0.0, 0.0);
    glTEXCOORD2f(0,0);
    glVERTEX3f(-thh, hh, 0.0);
    glTEXCOORD2f(0,2);
    glVERTEX3f(-thh, -hh, 0.5);
glEND;
glDISABLE(GL_TEXTURE_2D);

Now the REPEAT..UNTIL loop is entered which is delayed by 50 milliseconds by known SDL_DELAY. Each cycle the the scene gets rotated by 5 degrees around the y-axis by function glROTATEf. More details about this in Chapter 8.

The actual texturing of one of the four triangles of the tetrahedron is now described. Therefore 2d texturing has to be enabled by glENABLE(OGL capability). The capability we would like to enable is defined by GL_TEXTURE_2D.

Just as known from Chapter 8 the triangle mode is started by glBEGIN(geometric primitive type) with GL_TRIANGLES. Instead of a color we now define specific texture coordinates which should be allocated to specific vertices. glTEXCOORD2f(s coordinate, t coordinate) is used to define the coordinate of the texture we then allocate to a specific vertex. By the way, even though the official names of the texture coordinates are s and t, they can be considered as x and y values, which is more common for two-dimensional coordinate systems. The values for s and t are relative, so a value of 1 (= 100%) means the full width or height, independent of the actual width or height (32 x 32, 64 x 64, 128 x 256, …), a value of 2 (= 200%) then corresponds to two times the texture’s width or height. The coordinate (s, t) = (2, 2) is allocated to the vertex with the vertex coordinates (x, y, z) = (thh, 0.0, 0.0). Texture coordinate (0, 0) is allocated to vertex (thh, hh, 0.0). Texture coordinate (0, 2) is allocated to vertex (thh, hh, 0.5). Often this texturing process is compared to papering a wall, and indeed there are similarities. The vertex coordinates are exactly the same as for the first triangle in Chapter 8.

Finally the geometry definition and the texturing mode is finished by glEND and glDISABLE(OGL capability).

//drawing remaining three untextured faces
glBEGIN(GL_TRIANGLES);
    glCOLOR3f(0.0, 1.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
    glVERTEX3f(-thh, hh, 0.0);

    glCOLOR3f(1.0, 0.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, -hh, -0.5);

    glCOLOR3f(1.0, 1.0, 1.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
glEND;

SDL_GL_SWAPBUFFERS;
UNTIL keypressed;

The remaining three areas of the triangle are kept as in Chapter 8. Finally the display buffer if swapped after each cycle and the REPEAT..UNTIL loop stopped if a key is pressed in the console.

glDELETETEXTURES(1, ogl_texture);
DISPOSE(ogl_texture);
SDL_QUIT;
END.

Last but not least everything has to be free’s and closed as known. Anyway, the texture has to free’d by glDELETETEXTURES(number of textures, texture pointer). Then the pointer can be disposed as known and SDL can be quit.

Again, if you want to learn OpenGL and its capabilities to a more advaced extend you need to read more professional tutorials related to pure OpenGL programming. As a starting point I’d like to mention NeHe Productions’ OpenGL tutorials again, because they are professional and provide example code for JEDI-SDL for several lessons. 🙂

This file contains the source code: chap8a.pas (right click and “save as”)
This file is the executable: chap8a.exe (right click and “save as”)

The final result should look and behave like this: A tetrahedron consisting of three different coloured areas (cyan, magenta and white) and one textured area is spinning slowly around itself. When pressing a key in the console the show quits.

Result of JEDI-SDL Chapter 8a

Chapter 8: SDL and OpenGL – Entering the third dimension (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

This chapter will introduce you on how to combine the SDL library with the famous Open Graphics Library (OpenGL). OpenGL is the first choice when it comes to platform independent 2d and 3d graphic programming. As long as you just want to do 2d programming you can stay with SDL and there is no need to use OpenGL (even though OpenGL is also capable of doing 2d graphics). However, if your project needs 3d graphics you can set up your system for this quite easy using OpenGL in SDL. The main advantages of SDL here are that any other task except the graphics is further done by SDL (e.g. keyboard handling, sound,…) to keep the platform independence and ease. Further the setup of your SDL environment for the usage of OpenGL is very easy (compared to the setup without SDL) AND also is platform independent. Without SDL you would’ve to write different code to set up OpenGL for each operating system.

You need this software:

Software Version Source Description
OpenGL driver Usually your graphic card provides the corresponding OpenGL driver and you don’t have to do anything. And if so it is very likely that version 1.1 is fully supported. However if you are one of the few poor people whose graphic card doesn’t support OpenGL, check the graphic card’s manufacturer’s homepage for OpenGL drivers.

There is support for some others OpenGL related units, like GLUT (OpenGL Utility Toolkit) which provides a simple windowing application programming interface, GLX (OpenGL Extension to the X Window system) which provides a binding to use OpenGL in X Window windows (Linux), GLEXT (OpenGl Extensions) which provide additional functions since version 1.1 of OpenGL from 1996. I didn’t try out any of the latter mentioned units but if you progress in learning and using OpenGL especially GLEXT might get interesting to you since it provides the up-to-date functionality.

Now following the whole code at once as usual.

PROGRAM chap8;
USES CRT, SDL, GL, GLU;

VAR
userkey:CHAR;
screen:pSDL_SURFACE;
h,hh,th,thh:REAL;

BEGIN
//some calculations needed for a regular tetrahedron with side length of 1
h:=SQRT(0.75); //height of equilateral triangle
hh:=h/2;       //half height of equilateral triangle
th:=0.75;      //height of tetrahedron
thh:=th/2;     //half height of tetrahedron

SDL_INIT(SDL_INIT_VIDEO);

SDL_GL_SETATTRIBUTE(SDL_GL_RED_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SETATTRIBUTE(SDL_GL_DOUBLEBUFFER, 1);

screen:=SDL_SETVIDEOMODE(640, 480, 0, SDL_OPENGL);
IF screen=NIL THEN HALT;

glCLEARCOLOR(0.0, 0.0, 1.0, 0.0);
glVIEWPORT(0,0,640,480);
glMATRIXMODE(GL_PROJECTION);
glLOADIDENTITY;
gluPERSPECTIVE(45.0, 640.0/480.0, 1.0, 3.0);
glMATRIXMODE(GL_MODELVIEW);
glLOADIDENTITY;
glCLEAR(GL_COLOR_BUFFER_BIT);
glENABLE(GL_CULL_FACE);
glTRANSLATEf(0.0, 0.0, -2.0);

REPEAT
SDL_DELAY(50);

glROTATEf(5, 0.0, 1.0, 0.0);
glCLEAR(GL_COLOR_BUFFER_BIT );

glBEGIN(GL_TRIANGLES);
    glCOLOR3f(1.0, 1.0, 0.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);

    glCOLOR3f(0.0, 1.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
    glVERTEX3f(-thh, hh, 0.0);

    glCOLOR3f(1.0, 0.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, -hh, -0.5);

    glCOLOR3f(1.0, 1.0, 1.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
glEND;

SDL_GL_SWAPBUFFERS;
UNTIL keypressed;

SDL_QUIT;
END.

What this code will do is to switch to the OpenGL mode of SDL and draw a spinning tetrahedron. When you press a key the program aborts. However, some identifiers mentioned in the code are not related to SDL but OpenGL. Furthermore there are some functions which are related to OpenGL but are provided by SDL. Pure OpenGL identifiers begin with GL_ (e.g. GL_TRIANGLES) or gl (e.g. glCLEARCOLOR) whereas SDL provided OpenGL functions begin with SDL_GL_ (e.g. SDL_GL_SETATTRIBUTE).

PROGRAM chap8;
USES CRT, SDL, GL, GLU;

VAR
userkey:CHAR;
screen:pSDL_SURFACE;
h,hh,th,thh:REAL;

BEGIN
//some calculations needed for a regular tetrahedron with side length of 1
h:=SQRT(0.75); //height of equilateral triangle
hh:=h/2;       //half height of equilateral triangle
th:=0.75;      //height of tetrahedron
thh:=th/2;     //half height of tetrahedron

The program is called “chap8”. We use CRT as known from many former chapters to provide an easy way to recognize if a key got pressed. We need SDL for SDL support. New are the OpenGL units GL and GLU which are both included uncompiled in the JEDI unit package as well as pre-compiled along with the FPC compiler.

We need a variable “userkey” to recognize the user pressing a button. This is unrelated to SDL and OpenGL. The screen variable will display the scene later and is known from every former chapter. Four REAL variables are needed for some calculations regarding a tetrahedron.

The first four lines of code after BEGIN are needed to have some pre-calculated values at hand when it comes to constructing the tetrahedron later. h is the height of a equilateral triangle when each side has the length of one. hh corresponds to the half height meaning the value of h devided by two. th is the height of the tetrahedron (from any of the four possible bases to the corresponding peak) constructed of equilateral triangles. thh means the half value of th. These expressions result just from some geometry (Pythagorean theorem) and aren’t related directly to SDL or OpenGL.

SDL_INIT(SDL_INIT_VIDEO);

SDL_GL_SETATTRIBUTE(SDL_GL_RED_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SETATTRIBUTE(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SETATTRIBUTE(SDL_GL_DOUBLEBUFFER, 1);

screen:=SDL_SETVIDEOMODE(640, 480, 0, SDL_OPENGL);
IF screen=NIL THEN HALT;

First of all we initialize SDL as known. Attention now: Before setting up the video mode for OpenGL we have to set all needed attributes of the OpenGL environment. If you set them afterwards they won’t be recognized and default values are used by OpenGL. The function to do so is SDL_GL_SETATTRIBUTE(attribute, value). The function returns the integer 0 if setting was successful, -1 otherwise. The corresponding function to read out the set value of an attribute is SDL_GETATTRIBUTE(attribute, value) with the same error checking values. The attribute’s value is written to value. This last function is not used in the code.

The following table (Source: JEDI-SDL documentation) shows all possible attributes which can be set:

Attribute Description
SDL_GL_RED_SIZE Size of the framebuffer red component,
in bits
SDL_GL_GREEN_SIZE Size of the framebuffer green component,
in bits
SDL_GL_BLUE_SIZE Size of the framebuffer blue component,
in bits
SDL_GL_ALPHA_SIZE Size of the framebuffer alpha component,
in bits
SDL_GL_DOUBLEBUFFER 0 or 1, enable or disable double buffering
SDL_GL_BUFFER_SIZE Size of the framebuffer, in bits
SDL_GL_DEPTH_SIZE Size of the depth buffer, in bits
SDL_GL_STENCIL_SIZE Size of the stencil buffer, in bits
SDL_GL_ACCUM_RED_SIZE Size of the accumulation buffer red
component, in bits
SDL_GL_ACCUM_GREEN_SIZE Size of the accumulation buffer green
component, in bits
SDL_GL_ACCUM_BLUE_SIZE Size of the accumulation buffer blue
component, in bits
SDL_GL_ACCUM_ALPHA_SIZE Size of the accumulation buffer alpha
component, in bits
Source of table and content: JEDI-SDL documentation.

Each pixel of the screen contains three different colour components (red, green, blue). We want each pixel’s colour component’s size to be five bits. This is a default value for the colour components. The depth buffer (also called Z-buffer) will get 16 bits of size, also a default value. Finally we allow double buffering. Be careful here. The double buffering for an OpenGL scene is not enabled by the SDL flag SDL_DOUBLEBUF in the video set function but is set by SDL_GL_SETATTRIBUTE(SDL_GL_DOUBLEBUFFER, 1). For any other attributes, its meanings and its values refer to OpenGL tutorials which you find anywhere in the Internet.

screen:=SDL_SETVIDEOMODE(640, 480, 0, SDL_OPENGL);
IF screen=NIL THEN HALT;

The goal is to draw an OpenGL scene within a SDL environment to have access to all the advantages each library provides. So after setting all necessary attributes of the OpenGL scene you have to create a SDL window which is able to display an OpenGL scene. To set up such a window you use the common SDL_SETVIDEOMODE(parameters) function. The first two parameters determine the window’s size as known. The values for the width and the height should be consistent with the values for the OpenGL viewport which will be set later.

The third parameter determining the colour depth of a SDL scene should be ignored and set to 0 if you want to set up an OpenGL scene. The colour depth of an OpenGL scene is set up as shown before with SDL_GL_SETATTRIBUTES(parameters). However, any value different from 0 will not affect the OpenGL scene in any way.

To set up an OpenGL scene successfully you must add the SDL_OPENGL flag as shown. However, you can combine it with any other window appearance flag (e.g. fullscreen, without border, …) by using the logic operator OR as known.

glCLEARCOLOR(0.0, 0.0, 1.0, 0.0);
glVIEWPORT(0,0,640,480);
glMATRIXMODE(GL_PROJECTION);
glLOADIDENTITY;
gluPERSPECTIVE(45.0, 640.0/480.0, 1.0, 3.0);
glMATRIXMODE(GL_MODELVIEW);
glLOADIDENTITY;
glCLEAR(GL_COLOR_BUFFER_BIT);
glENABLE(GL_CULL_FACE);
glTRANSLATEf(0.0, 0.0, -2.0);

The following descriptions regard to pure OpenGL programming. Detailed descriptions you’ll find in any OpenGL tutorial. However, I will give a brief overview over these functions.

glCLEARCOLOR(parameters) sets the background colour used if you call glCLEAR to reset the colour buffer. glCLEARCOLOR(parameters) expects four float point values corresponding the red, blue, green colour and the alpha channel (for transparency). Any value is a number between 0.0 and 1.0 where 0.0 means no addition of the corresponding colour component or transparency and 1.0 full addition of the corresponding colour component or complete transparency. In the example code we set up a completly blue background without transparency.

glVIEWPORT(parameters) sets the viewport of the OpenGL scene. The first two parameters are integer x- and y-values for the actual position in space, 0/0 is the default setting. The lower-left corner of the viewport is therefore at position 0/0 by default. The next two parameters define the width and height of the scene’s projection to the physical screen. Remeber please, these two values should be the same as has been used for the window width and height when using SDL_SETVIDEOMODE(parameters). However, it isn’t forbidden to use different values. We use a width of 640 and height of 480 pixels.

glMATRIXMODE(parameter) sets the matrix which you’d like to manipulate. It allows different parameters. Very common values are GL_PROJECTION and GL_MODELVIEW, further parameters are GL_TEXTURE and GL_COLOR. The following matrix operations (glTRANSLATEf, glROTATEf,…) are applied to the set matrix’ stack. Often this function is followed by glLOADIDENTITY. glLOADIDENTITY replaces the current matrix by the identity matrix. All elements of the identity matrix equal 0 except for the diagonal elements which are 1.

First we need to specify the projection transformation. Therefore we set the matrix mode by parameter GL_PROJECTION. Either we choose a perspective projection that reflects a 3d world, so objects with a great z value appear smaller than objects with a lower z value, or we choose an orthographic projection that reflects a 2d world, so objects will be drawn independently of their z value. The latter mode is usually used for text, menus, buttons, even complete side scrolling 2d games. Since we’d like to render a tetrahedron, we set up a persepctive projection matrix by gluPERSPECTIVE(parameters). The prefix “glu” indicates it’s a function from the OpenGL utilities unit. Instead we also could have used the glFRUSTUM(parameters) function which is part of the core OpenGL unit to generate such a matrix but gluPERSPECTIVE(parameters) makes it easy to get a matrix for distortionless display of the resulting projection. The first parameter is the field of view angle (fovy) in degrees. The second parameter is the aspect, usually the aspect ratio of the window dimensions width/height, here 640/480. The last two parameters are the (positive) z values for the near and far clipping plane of the generated viewing frustum. The generated perspective projection matrix corresponds to a viewing frustum.

Next we have to set up the viewing and modeling transformation. We switch to this matrix mode by GL_MODELVIEW as argument for GL_MATRIXMODE(parameter). We replace the matrix stack with the identity matrix. Now any subsequent matrix operations (glTRANSLATEf, glROTATEf,…) are applied to this matrix. It is replaced by the identity matrix and ready for further manipulations. This mode lets you manipulate the actual objects or models, e.g. a tetrahedron.

The color buffer gets cleared by glCLEAR(GL_COLOR_BUFFER_BIT). Then back-face culling is enabled by GL_ENABLE(GL_CULL_FACE). If you draw e.g. a triangle it has two faces. A front face and a back face. In a tetrahedron consisting of four triangles the inner faces are never seen. To avoid drawing them the back-face culling gets enabled so OpenGL isn’t drawing them. Finally the scene gets translated along the z-axis about -2.0 units by glTRANSLATEf(parameters) with x, y and z as parameters.

REPEAT
SDL_DELAY(50);

glROTATEf(5, 0.0, 1.0, 0.0);
glCLEAR(GL_COLOR_BUFFER_BIT );

glBEGIN(GL_TRIANGLES);
    glCOLOR3f(1.0, 1.0, 0.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);

    glCOLOR3f(0.0, 1.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
    glVERTEX3f(-thh, hh, 0.0);

    glCOLOR3f(1.0, 0.0, 1.0);
    glVERTEX3f(thh, 0.0, 0.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, -hh, -0.5);

    glCOLOR3f(1.0, 1.0, 1.0);
    glVERTEX3f(-thh, -hh, 0.5);
    glVERTEX3f(-thh, hh, 0.0);
    glVERTEX3f(-thh, -hh, -0.5);
glEND;

SDL_GL_SWAPBUFFERS;
UNTIL keypressed;

SDL_QUIT;
END.

Don’t be worried. This piece of code looks big but this is just because of the definition of the actual tetrahedron. First the REPEAT loop is entered. Every cycle is delayed by 50 milliseconds by the known SDL_DELAY(time in ms) function. Next the scene gets rotated. This can be achieved by the OpenGL function glROTATEf(parameters). You’ve to pass four arguments. The degree of rotation first, then three values which define the orientation of the axis around which is rotated. A triple (1.0 / 0.0 / 0.0) or (0.0 / 1.0 / 0.0) or (0.0 / 0.0 / 1.0) corresponds to a rotation around a x- or y- or z-axis respectivly. However, by linear combination of all the three components you can orient the rotation axis completly free in 3d space. For the tutorial with each cycle of the loop the tetrahedron is rotated by 5 degrees around the y axis. The color buffer gets cleared so the tetrahedron from the previous cycle disappears before drawing a new one (rotated by 5 degrees).

Now we need to construct the tetrahedron. Such constructions of objects is always done between a glBEGIN and glEND clause. Yes, even C/C++ programmers have to use Pascal’s own best BEGIN/END clause here :). Well, the glBEGIN (geometric primitive type) command needs an argument which defines what type of primitive is drawn. In our case we want to draw triangles, so we use GL_TRIANGLES here. Further common arguments are GL_POINTS, GL_LINES, GL_QUADS and GL_POLYGON, for points, lines, quads and polygons. Depending of this setting the vertices given within the glBEGIN/glEND block are interpreted. If you chose GL_TRIANGLES, three vertices are interpreted to form one triangles. However, for GL_LINES, only two vertices are needed. The first and the second vertex from our example would form one line. The third vertex would be interpreted to be the starting point for a new line, the first vertex of the next triangle block would be used as second point to form the second line and so on. Note, for 3d games usually triangles are used since most graphic cards seem to be optimized to draw them.

A tetrahedron consists of four triangular areas. Each trianglular area has three corners, these corners are called vertices in OpenGL. Within the glBEGIN/glEND clause the code has four blocks, each containing three glVERTEX3f(coordinates) and one glCOLOR3f(RGB components) commands. Each block creates one triangular area of the tetrahedron consisting of 3 vertices. Every triangular area also gets its own colour. For glCOLOR3f(RGB components) you use the three normalized colour codes for the red, green and blue component. 1.0 corresponds to 255 and means full addition of this colour component, 0.0 corresponds to 0 and means no addition of this colour component. As you may have noticed in the example always two colour components are used, e.g. red and green leads to yellow. You may prefer other combinations.

For the actual creation of a geometric primitive, like a triangle, just place the corresponding number of vertices in the 3d space and OpenGL will do the rest. It is important though to consider the order of placing the vertices. The order of placing the vertices determines what will be the front and back of the triangles. If you don’t consider this you may end up with curious results after doing back-face culling as described. Remeber: Back-face culling means, that the back of each primitive isn’t drawn. The way the triangles are created in the examples all areas have their front showing while their back is inside the tetrahedron. So without hestiation you can leave them undrawn. Imagine though what would be the result if one of the triangles would have been flipped because of placing the vertices in the wrong order. The undrawn back would show up. The impression of a rotating tetrahedron would be messed up. To get the front showing up instead of the back you have to place the vertices anticlockwise! The arguments for glVERTEX3f(x, y, z coordinate) are just the cartesian coordinates of the vertex you want to place.

Since I decided to give a rather complicated example for this tutorial (usually OpenGL tutorials explain this by cubes and six quads) I will examine the code here in more detail for the first triangle. If you’ve understood it, you’ll do cubes just by the way :). The coordinates for the first vertex are (thh / 0 / 0), the next vertex are (-thh / hh / 0) and the last vertex are (-thh / -hh / 0.5). The following image will show you how the vertices are placed in the cartesian space.

Coordinates in OpenGLAs you can check quickly, the vertices are indeed placed anticlockwise. The origin of the coordinate system (0 / 0 / 0) is approximately in the center of the triangle, actually it is in the center of the tetrahedron, when it is finished. The first vertex is placed onto the x axis at thh, the half height of the tetrahedron. The second vertex is placed on the opposite x position of the origin at -thh and has a y value of hh, the half height of an equilateral triangle. This point is still in the x/y-plane formed by the x and y axis. The last vertex has a x value of -tth and and y value of -hh, because of the z value of 0.5 this vertex doesn’t belong to the x/y-plane the other two vertices belong to. It is in front of this plane. You’ll notice a slight distorted appearance of the triangle for the third vertex, just as if it would bend towards you, that is because it actually is.

Now try to check if you understand the other vertices coordinates and if you understand why their front and not their back is showing. The loop’s last command is SDL_GL_SWAPBUFFERS which corresponds to SDL_FLIP for a pure SDL scene. It refreshes the scene. In case you have an OpenGL scene you never use SDL_FLIP or SDL_UPDATERECT to refresh the scene!

The loop quits if a key gets pressed. Then SDL and the Pascal program quit as known. If you want to learn OpenGL and its capabilities to a more advaced extend you need to read more professional tutorials related to pure OpenGL programming. As a starting point I’d like to mention NeHe Productions’ OpenGL tutorials, because they are professional and provide example code for JEDI-SDL for several lessons.

This file contains the source code: chap8.pas (right click and “save as”)
This file is the executable: chap8.exe (right click and “save as”)

The final result should look and behave like this: A tetrahedron consisting of four different coloured areas (yellow, cyan, magenta and white) is spinning slowly around itself. When pressing a key in the console the show quits.

Result of JEDI-SDL Chapter 8

Chapter 5: Text and font handling (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

This chapter will introduce you on how to load fonts and write to the screen. The ability for this is not implemented in the original SDL library itself but Sam Lantinga provides an add-on to SDL called SDL_TTF to work with texts based on the FreeType project and their FreeType 2.0 release. Fortunately the JEDI-SDL project also provides this unit for Free Pascal called SDL_TTF as well. So for preparation we have to do three things (sorry the following instructions are for Windows only but should be similar for Linux system as well. The examples after the three steps of installing should work for Linux, too.):

You need this .dll file:

Software Version Source Description
SDL_ttf-2.0.10-win32.zip 2.0.10 http://www.libsdl.org/projects/SDL_ttf/ This is the corresponding dynamic link library file.

You should extract the zip-file and get two files. A text file and the important SDL_ttf.dll. Analogous to SDL.dll in chapter 1 you have to copy them to the system32-folder. If you forget this and run the examples below you will get an error with exitcode = 309.

First of all we want to discuss the principle behind adding text to the screen within the SDL environment. Any text which is written to the screen is a simple surface itself which gets blit to the screen surface as done with graphics seen in previous chapters. In practice this means we need the screen surface again and a surface onto which can be written (here: “fontface”). The text content in the fontface surface will then be blitted to the screen surface. A simple diagram may illustrate this.

The concept of using fontsYou probably wondered already how to get the text to the “fontface” though. Exactly therefore we need the new SDL_TTF unit which has to be loaded by adding SDL_TTF to the uses clause. It provides the command TTF_RENDERTEXT_…(font, text, colour) which creates a surface with a given font, text and colour. This procedure is illustrated by the first arrow in the diagram. In fact there is not just one but many different TTF_RENDERTEXT_… modes which differ (hinted at by the three dots) in arguments list, quality, render speed and other properties. The following table will give you an overview. For dynamic ingame text or chats the solid mode is the best choice since it is the fastest rendering mode and also provides simple transparency. The following table will give you a brief overview of the modes and their properties.

Function Transparency Antialiasing Colour depth and format Quality Speed
TTF_RENDERTEXT_… in general creates surfaces with the given text (of type pChar) in ISO 8859-1 (Latin1) format; analogous you can use TTF_RENDERUTF8_… to get the the text in the corresponding UNICODE Transformation Format 8.
TTF_RENDERTEXT_SOLID(font, text, colour); yes (colorkey, 0 pixel) no 8-bit palettized (RGB) low very fast
TTF_RENDERTEXT_SHADED(font, text, colour1, colour2); no (0 pixel is background colour) yes 8-bit palettized (RGB) high fast
TTF_RENDERTEXT_BLENDED(font, text, colour); yes (alpha channel) yes 32-bit unpalettized (RGBA) very high slow
TTF_RENDERGLYPH_… in general creates surfaces with the corresponding UNICODE glyph letter (of type WORD); analogous you can use TTF_RENDERUNICODE_… to get the corresponding UNICODE letter but attention: you have to give the letter’s number as pointer, so number is of type ^WORD.
TTF_RENDERGLYPH_SOLID(font, number, colour); yes (colorkey) no 8-bit palettized (RGB) low very fast
TTF_RENDERGLYPH_SHADED(font, number, colour1, colour2); no (0 pixel is background colour) yes 8-bit palettized (RGB) high fast
TTF_RENDERGLYPH_BLENDED(font, number, colour); yes (alpha channel) yes 32-bit unpalettized (RGBA) very high slow

All of the given commands are functions and will return the NIL pointer (instead of the new surface) if the text or letter rendering failed. Let’s have a look at the code.

PROGRAM chap5;

USES SDL, SDL_TTF;

VAR
screen, fontface:pSDL_SURFACE;
loaded_font:pointer;
colour_font, colour_font2:pSDL_COLOR;
i:BYTE;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(400,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;


IF TTF_INIT=-1 THEN HALT;
loaded_font:=TTF_OPENFONT('C:\WINDOWS\fonts\arial.ttf',40);

NEW(colour_font);
NEW(colour_font2);
colour_font^.r:=255; colour_font^.g:=0;    colour_font^.b:=0;
colour_font2^.r:=0;  colour_font2^.g:=255; colour_font2^.b:=255;

fontface:=TTF_RENDERTEXT_SHADED(loaded_font,'HELLO WORLD!',colour_font^,colour_font2^);


SDL_BLITSURFACE(fontface,NIL,screen,NIL);
SDL_FLIP(screen);
READLN;

DISPOSE(colour_font);
DISPOSE(colour_font2);
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
TTF_CLOSEFONT(loaded_font);
TTF_QUIT;
SDL_QUIT;
END.

As always now we look at the code step by step.

PROGRAM chap5;

USES SDL, SDL_TTF;

VAR
screen, fontface:pSDL_SURFACE;
loaded_font:pointer;
colour_font, colour_font2:pSDL_COLOR;
i:BYTE;

The program is initilized with the name “chap5”. The unit SDL as well SDL_TTF has to be given in the uses clause! Remember please that the True Type Font system is an individual separate project and therefore you have to add this unit separately.

There are two pSDL_SURFACE variables. screen is for displaying at the physical screen as known, fontface will store the generated text by the True Type Font system before it is blitted to the screen finally. loaded_font will store font and is of pointer type. colour_font and colour_font2 are storing the text and background colour of the generated text later, they are of a specific SDL colour type which will be discussed in more detail later. i is a counting variable as known.

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(400,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;


IF TTF_INIT=-1 THEN HALT;
loaded_font:=TTF_OPENFONT('C:\WINDOWS\fonts\arial.ttf',40);

First the SDL system has to initilized as known from all the previous chapters. To initialize the TTF (True Type Font) system you have to use TTF_INIT which returns -1 if something failed. Notice again that the whole true type support is an own additional project (FreeType project) to the SDL library, so this cannot to be initilized by the SDL_INIT command.

To load a certain font you use TTF_OPENFONT(font,point size), or more specific TTF_OPENFONT(const filename:PCHAR; ptsize:INTEGER):pTTF_FONT. This command is a function that returns a usual pointer (which pTTF_FONT actually is)! The parameters are the absolute(!) path to the font (e.g. C:\WINDOWS\fonts\arial.ttf) and the point size which detemines the size of the letters. These font information are accessed by the loaded_font pointer initially defined.

NEW(colour_font);
NEW(colour_font2);
colour_font^.r:=255; colour_font^.g:=0;    colour_font^.b:=0;
colour_font2^.r:=0;  colour_font2^.g:=255; colour_font2^.b:=255;

So next we have to determine the colour of the letters and the message itself. The colour is determined by a pSDL_COLOR record, which is of course kind of pointer. In pSDL_COLOR record there are three elements (actually four, but the forth is unused) which can be accessed by .r,.g and .b and determine as common the shares of red, green and blue colour. You will agree that writing a function allocation of RGB triples can be senseful, especially if you have to handle many different colours but for our example we won’t do this though since we just have to define two colours. The fact that pSDL_COLOR is of pointer type needs us to set colour_font and colour_font2 up by NEW command and free it finally by DISPOSE. These commands you should be familiar with because they are usual Pascal commands.

fontface:=TTF_RENDERTEXT_SHADED(loaded_font,'HELLO WORLD!',colour_font^,colour_font2^);

Now we use TTF_RENDERTEXT_SHADED(font, text, colour1, colour2), or more specific TTF_RENDERTEXT_SHADED(font:pTTF_FONT; const text:PCHAR; fg:tSDL_COLOR; bg:tSDL_COLOR):pSDL_SURFACE, which we introduced recently (check the table). It returns a pSDL_SURFACE. We return it to fontface. The parameters are the used font as pointer (loaded_font), the message string and the colours (colour_font, colour_font2). colour_font will provide the foreground colour which should be red since we defined R=255, G=0, B=0. The background should get cyan since we defined R=0, G=255, B=255. If the rendering process works fine we have the text in the specified colours in fontface.

SDL_BLITSURFACE(fontface,NIL,screen,NIL);
SDL_FLIP(screen);
READLN;

DISPOSE(colour_font);
DISPOSE(colour_font2);
SDL_FREESURFACE(screen);
SDL_FREESURFACE(fontface);
TTF_CLOSEFONT(loaded_font);
TTF_QUIT;
SDL_QUIT;
END.

The fontface surface is a usual SDL surface so you can now do every manipulation you want or at least blit it to the screen surface and display the text after updating/flipping.

Finally all the variables and surfaces have to disposed as known. Like every SDL surface has to be free’d and SDL system has to be quit so has the TTF system. The procedures TTF_CLOSEFONT(font:pTTF_FONT) and TTF_QUIT have to be used to do this.

Now you are able to write ;).

This file contains the source code: chap5.pas (right click and “save as”)
This file is the executable: chap5.exe (right click and “save as”)

The final result should look and behave like this: The text “Hello World!” is displayed having red letters on cyan background.

Result of JEDI-SDL Chapter 5

Chapter 3a: Displaying different picture formats (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

In the previous chapter it was shown how to load bitmap images in the bitmap format. You may have wondered if there is a possibility to load other formats than bitmap images. The JEDI-SDL package provides a unit called SDL_IMAGE which exactly is created for this purpose. The following formats you can load according to the unit’s C/C++ documentation: TGA, BMP, PNM, XPM, XCF, PCX, GIF, JPG, TIF, LBM, PNG. I had troubles loading XCF images which is probably due to the fact this format is not standardized.

You need this .dll file to work successfully with the new unit:

Software Version Source Description
SDL_image-1.2.7-win32.zip 1.2.7 http://www.libsdl.org/projects/SDL_image/ This is the corresponding dynamic link library file for unit and image formats.

You should extract the zip-file and get six files. A text file, the important SDL_image.dll and several image format DLLs. Analogous to SDL.dll in chapter 1 you have to copy them to the system32-folder. If you forget this and run the examples below you will get an error with exitcode = 309.

After installation of the unit we now can proceed to the source code.

PROGRAM chap3;
USES SDL, SDL_IMAGE, STRINGS;

CONST
//add the path to your files here
picturepath:PCHAR = 'C:\FPC\2.2.4\my_images\fpsdl.';

VAR
screen:pSDL_SURFACE;
picture: ARRAY[0..2] OF pSDL_SURFACE;
fileextension: ARRAY[0..2] OF PCHAR;
filepath: ARRAY[0..2] OF PCHAR;
i:BYTE;

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;


fileextension[0]:='png';
fileextension[1]:='jpg';
fileextension[2]:='tif';

FOR i:=0 TO 2 DO
BEGIN
  filepath[i]:=STRNEW(picturepath);
  filepath[i]:=STRCAT(filepath[i],fileextension[i]);

  picture[i] := IMG_LOAD(filepath[i]);
  IF picture[i]=NIL THEN HALT;

  SDL_BLITSURFACE(picture[i],NIL,screen,NIL);
  SDL_FLIP(screen);
  READLN;
END;

FOR i:=0 TO 2 DO
BEGIN
  SDL_FREESURFACE(picture[i]);
  STRDISPOSE(filepath[i]);
END;
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

The code shows the same picture known from chapter 3 in three different formats: PNG, JPG and TIF. You should download them (right-click onto each image and save) and put them at a desired location of your hard drive. Don’t worry if the tif image isn’t shown properly in your web browser. Since it isn’t a native web image format most web browsers don’t support it. However you can still download it.

tif image - usually not shown
TIF image – usually not supported natively by browsers to be displayed
Free Pascal example image in different formats
JPEG images
fpsdl
PNG image
PROGRAM chap3;
USES SDL, SDL_IMAGE, STRINGS;

First we need to include the SDL unit, of course we need also the SDL_IMAGE unit. The STRINGS unit is needed to handle file pathes a simple and short way. The latter unit is not related to the SDL library.

CONST
//add the path to your files here
picturepath:PCHAR = 'C:\FPC\2.2.4\my_images\fpsdl.';

VAR
screen:pSDL_SURFACE;
picture: ARRAY[0..2] OF pSDL_SURFACE;
fileextension: ARRAY[0..2] OF PCHAR;
filepath: ARRAY[0..2] OF PCHAR;
i:BYTE;

Now we define a constant, which contains the absolute path to the image files. The path is C:\FPC\2.2.4\my_images\. Of course you are free to choose any other location. Each file is named “fpsdl.” and the corresponding extension, like “bmp” for bitmap image, “png” for portable network graphics image, and so on. They all lie in the folder my_images.

In the variables part we define the screen surface again and an array of picture surfaces, all of kind pSDL_SURFACE. The screen surface decides what is shown at the display and the picture surfaces will store the images from image files of different formats. Next two arrays for the file extensions of the different image formats (bmp, png,…) and the image file paths are defined and of kind PCHAR. Finally we define the counter variable i.

BEGIN
SDL_INIT(SDL_INIT_VIDEO);
screen:=SDL_SETVIDEOMODE(200,200,32,SDL_SWSURFACE);
IF screen=NIL THEN HALT;


fileextension[0]:='png';
fileextension[1]:='jpg';
fileextension[2]:='tif';

As usual we start by initating the SDL library, defining a 200 x 200 pixels window and check if it was successful. The next part defines the different file extensions we want to use. In this example we will show the ability of SDL_IMAGE to load png, jpg and tif images. Feel free to try out all the other common formats by extending this demo program. Again I’d like to mention that I experienced troubles loading GIMP’s images in XCF format.

FOR i:=0 TO 2 DO
BEGIN
  filepath[i]:=STRNEW(picturepath);
  filepath[i]:=STRCAT(filepath[i],fileextension[i]);

  picture[i] := IMG_LOAD(filepath[i]);
  IF picture[i]=NIL THEN HALT;

  SDL_BLITSURFACE(picture[i],NIL,screen,NIL);
  SDL_FLIP(screen);
  READLN;
END;

The presented loop is the core of this demo program. It shows the preparation of the file locations and names, the creation of the image surfaces and the final blit to the screen surface to display them.

The loop will be cycled three times, once for each file. First we reseve some space on the heap and insert the constant file string by STRNEW. Next we concatenate the file path and the extension by STRCAT. For these operations we needed to include the STRINGS unit. Both commands aren’t related to the SDL library.

Then IMG_LOAD(absolute path/image file) function allows to load image data from image files with many different image formats to a surface. That is the key function of this demo program. However, be aware that the image data loaded to the surface is not compressed anymore and is stored in the usual RGBA format of SDL surfaces. The function returns NIL if it failed.

Since the example pictures have the same dimensions as the screen surface they can be blitted easily by SDL_BLITSURFACE as shown. The screen gets refreshed by SDL_FLIP. Both commands you know from the recent chapter. Finally we wait for the user to press enter after each cycle.

FOR i:=0 TO 2 DO
BEGIN
  SDL_FREESURFACE(picture[i]);
  STRDISPOSE(filepath[i]);
END;
SDL_FREESURFACE(screen);
SDL_QUIT;
END.

Now we have to clean up anything. The loop disposes the picture surfaces and the path strings. Finally the screen surface gets disposed and the SDL environment quit by SDL_QUIT. Except for STRDISPOSE you should remember any command from previous chapters. STRDISPOSE is a command from STRING unit, so again no SDL library related command.

This file contains the source code: chap3a.pas (right click and “save as”).
For this chapter no executable is provided.

Hope you are successful and have fun.

The final result should look and behave like this: First the “Free Pascal meets SDL” image appears in one of the the image formats (different of bitmap). Everytime you press return (or enter) in the console the same image with another format is displayed until the program quits.

Result of JEDI-SDL Chapter 3a

Chapter 1: Introduction, installation and configuration (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

The introduction is short. SDL library gives you the ability to develop powerful applications for many different operating systems (OS, e.g. Linux, Windows, MacOS) only by learning one set of commands. The SDL library takes on the assignment to translate your commands to the specific OS commands. It’s especially meaningful to use the SDL library if you plan to develop two-dimensional games or similar software. Role-playing-, Real time/turn based strategy-, side-scrolling-, arcade-, board-, card-, simulation-, multi-user dungeon-, puzzle games and so on are possible. SDL helps you to set up easily an SDL/OpenGL environment which allows for creation of 3d applications. This finally allows even for three-dimensional games.

Now something about the licensing: Both, Free Pascal and the SDL library are free and your created work can be used in commercial programs! The license of Free Pascal is: modified LGPL and the license of SDL: GNU LGPL.

By this the introduction to SDL is finished already. Before you accidently proceed reading the manual installation of the SDL units for the Free Pascal compiler (FPC) I’d like to express that since version 2.2.2 of FPC the pure, pre-compiled JEDI-SDL units are released along with the compiler so actually there is no need to install the SDL units manually anymore. Keep in mind though, many chapters assume you did the manual installation, so there are some envornment settings described you can skip. You don’t have to set individual pathes to the different unit files. However, what you always HAVE TO consider is the download and copy of the corresponding DLL files described in the chapters. If you already have installed Free Pascal (version 2.2.2 or later) just go on with Chapter 1a now.

If you decide to do the manual installation there are two different unit packages which allow the development of SDL applications with the Free Pascal environment. If you don’t know which to choose I recommend strongly to choose the package of the JEDI-SDL Project! It is more advanced and supports a greater variety of features. This is also the package which is included in FPC nowadays.

LINUX USERS: The following description of the configuration is related to the MS Windows OS. The further chapters (except chapter 1 and 1a) will work for Linux OS, too. (If you want to contribute a configuration tutorial for Linux OS let me know; you would be mentioned as author of course.)

Which package of SDL units you want to install (MS Windows OS)?

JEDI-SDL (recommended)

SDL4FP

Now this chapter concerns on the installation of all necessary software on your system for programming SDL applications with Free Pascal on Windows XP (probably this will work for other Windows systems as well; please report if you tried out; what about Vista?).

First of all, we have to consider which software is needed. We need three different software packages. We need the Free Pascal compiler of course. Furthermore we need the original SDL library files and finally we need something what translates our Pascal programs to the SDL library (written in C/C++). This will be done by a few units. These units come from the JEDI-SDL Project (Joint Endeavor of Delphi Innovators – Simple Direct Media Layer Project).

This table provides all information you need about the files you need to download with respect to installation steps 1) – 3).

Software Version Size, MB Source Description
fpc-2.2.4.i386-win32.exe 2.2.4 ~35 http://www.Free Pascal.org Download the latest stable Free Pascal compiler.
JEDI-SDLFullSetup.exe
(Full Installer!)
1.0 Final RC2 ~5 Main site:http://jedi-sdl.pascalgamedevelopment.com/
Downloads:http://sourceforge.net/forum/forum.php?forum_id=724777
Download the latest version of JEDI-SDL package.
SDL-1.2.13-win32.zip 1.2.13 ~0.1 http://www.libsdl.org (German readers may prefer: http://www.libsdl.de/) Download the latest stable runtime library of SDL.

1) Download the latest stable Free Pascal compiler, version 2.2.4 or higher.

2) Download the latest version of JEDI-SDL, version 1.0 Final or higher. Be sure it is the full installer (not headers only or demos only).

3) Download the latest version of SDL runtime library, version 1.2.13 or higher.

Of course, if you have installed Free Pascal already you haven’t to download it again and you can skip step 4.

4) Execute “fpc-2.2.4.i386-win32.exe” to install Free Pascal. Let the self-installer create a shortcut on your desktop. Don’t modify any checked options during installation process. The default path is: ‘C:\FPC\2.2.4\’.

5) Extract “SDL-1.2.13-win32.zip” to get the SDL runtime library. This leads to two extracted files. There is a text file and the very important SDL.dll.

6) Copy those files (especially the SDL.dll!) to your
system32-folder! Usually you find it at “C:\WINDOWS\system32\”.
Don’t confuse it with the similar system-folder. (This works for
WinXP, probably for NT-series and Win2000 as well; if you use Win9x
or WinME you should copy it to system-folder instead of system32-folder)

If it isn’t possible for any reason to copy the files into this folder you later have to copy that file into the same folder where the application is. Now you have installed Free Pascal and the SDL runtime library on your system. Finally JEDI-SDL has to be installed.

7) Execute the “JEDI-SDLFullSetup.exe” and follow the installer’s instructions. Since you should avoid a folder name like “JEDI-SDL Full” which is suggested by the install program when asking for the install path, I suggest as full path: “C:\FPC\2.2.4\units\JEDI\”. All later tutorials will assume you installed to this path.

Now the compiler has to be told about where to find the new units.

8) Open the Free Pascal IDE (for example by clicking the shortcut on desktop).

9) In the menue choose the following item “Options”. From “Options” choose “Directories…”. Now a window should pop up.

10) The first tab whitin this new window is called “Units”. Here you add the full path to your SDL-units (e.g. C:\FPC\2.2.0\units\JEDI\SDL\Pas) below the other pathes. Leave the last backslash out. Make sure the path leads to the folder, where the sdl.pas file is located! This file contains all the basic features of SDL.

JEDI-SDL Directories

The picture doesn’t show the correct path! It is old. We use “C:\FPC\2.2.4\units\JEDI\SDL\Pas” instead. Confirm by clicking “OK”.

Congratulations! You have configured your system for developing SDL applications with Free Pascal! Now let’s check if it really was that easy…

11) To check if you installed your system successfully download the file demo02.pp (right click and “save as”) into demo-folder (C:\FPC\2.2.4\demo\) or anywhere else.

12) Open this file and run it.

If you see some cool colourful red and blue lines you have done everything as it has to be done :)! Have fun with your configured system!

For those of you who try running the demo and get an abortion together with a messege saying “exitcode = 309”: You skipped step 6! Did you copy the SDL.dll to your system32- or system-folder respectively? If so and the error still occurs you should copy the SDL.dll into the folder where the demos are placed. Now it should work.

VERY IMPORTANT

Since most users will not install FPC/SDL manually I removed the information about setting up the unit pathes in the compiler settings part of each individual chapter. However for you, without setting the unit pathes in your IDE you will get errors. Next is a list of the settings for each chapter where necessary IF you do the manual installation.

Chapter 3a

1) Make sure your compiler finds the new unit. (IDE: Options –> Directories… –> Units)

JEDI-SDL Directories for font handling2) Copy the file “jedi-sdl.inc” from the “../SDL/Pas”-folder into the “../SDL_image/Pas/”-folder. Both folders are located in the JEDI-SDL project folder (e.g. C:\FPC\2.2.4\units\JEDI\). If you forget this you will get an error on compiling saying SDL_IMAGE is not finding jedi-sdl.inc.

3) You need this .dll file:

Software Version Source Description
SDL_image-1.2.7-win32.zip 1.2.7 http://www.libsdl.org/projects/SDL_image/ This is the corresponding dynamic link library file for unit and image formats.

Chapter 5

1) Make sure your compiler finds the new unit. (IDE: Options –> Directories… –> Units)

JEDI-SDL Directories for font handling2) Copy the file “jedi-sdl.inc” from the “../SDL/Pas”-folder into the “../SDL_tff/Pas/”-folder. Both folders are located in the JEDI-SDL project folder (e.g. C:\FPC\2.0.4\units\JEDI\). If you forget this you will get an error on compiling saying SDL_TFF is not finding jedi-sdl.inc.

3) You need this .dll file:

Software Version Source Description
SDL_ttf-2.0.10-win32.zip 2.0.10 http://www.libsdl.org/projects/SDL_ttf/ This is the corresponding dynamic link library file.

Chapter 7

1) Make sure your compiler finds the new units. (IDE: Options –> Directories… –> Units) For the usage of SDL_MIXER you have to include both, the SDL_MIXER itself and the SMPEG unit (which is used by SDL_MIXER unit). The SMPEG unit actually is only used for mp3 support but your programs will fail to run even if you don’t use mp3 files.

JEDI-SDL Directories for sound handling2) Copy the file “jedi-sdl.inc” from the “../SDL/Pas”-folder into the “../SDL_Mixer/Pas/”-folder and “../smpeg/Pas/”-folder. All folders are located in the JEDI-SDL project folder (e.g. C:\FPC\2.0.4\units\JEDI-SDLv1.0\). If you forget this you will get an error on compiling saying SDL_Mixer or smpeg is not finding jedi-sdl.inc.

3) You need this .dll file and a music and sound file:

Software Version Source Description
SDL_mixer-1.2.8-win32.zip 1.2.8 http://www.libsdl.org/projects/SDL_mixer/ This is the corresponding dynamic link library file.
In my mind.ogg In-my-mind.ogg A music file. “In my mind” by First. Of course you could also use any other music file you desire.
dial.wav dial.wav Public domain sounds: http://www.pdsounds.org. Of course you could also use any other sound file you desire.

Chapter 8

1) Make sure your compiler finds the new units. (IDE: Options –> Directories… –> Units) You will need to add the path to the OpenGL units GL and GLU. GL provides the whole basic OpenGL functionality and GLU (OpenGL Utilities) provides some additional functions which are very useful but not provided as basic functions of OpenGL (version 1.1).

JEDI-SDL Directories for OpenGL support2) Copy the file “jedi-sdl.inc” from the “../SDL/Pas”-folder into the “../OpenGL/Pas/”-folder. All folders are located in the JEDI-SDL project folder (e.g. C:\FPC\2.2.4\units\JEDI\). If you forget this you will get an error on compiling saying GL or GLU is not finding jedi-sdl.inc.

3) You need this software:

Software Version Source Description
OpenGL driver Usually your graphic card provides the corresponding OpenGL driver and you don’t have to do anything. And if so it is very likely that version 1.1 is fully supported. However if you are one of the few poor people whose graphic card doesn’t support OpenGL, check the graphic card’s manufacturer’s homepage for OpenGL drivers.

Chapter 7: Sound and Music (JEDI-SDL)

This is an SDL 1.2 chapter. SDL 1.2 is obsolete since it has been replaced by SDL 2.0. Unless you have good reasons to stay here you may prefer to go for the modern SDL 2.0 :-).

This chapter will introduce you on how to load music and sounds since these are key features of every game and many applications. An easy way to play music (.wav, .mod, .mid, .ogg, .mp3) and sound files (.wav, .aif, .ogg, .voc) is the usage of an additional unit called SDL_MIXER. This unit is maintained by the same author which also provides the core SDL library, Sam Lantinga and the co-authors Stephane Peter and Ryan Gordon. Fortunately the JEDI-SDL team translated this unit so it is available for Pascal programmers.

You need this .dll file and a music and sound file:

Software Version Source Description
SDL_mixer-1.2.8-win32.zip 1.2.8 http://www.libsdl.org/projects/SDL_mixer/ This is the corresponding dynamic link library file.
In my mind.ogg In-my-mind.ogg A music file. “In my mind” by First. Of course you could also use any other music file you desire.
dial.wav dial.wav Public domain sounds: http://www.pdsounds.org. Of course you could also use any other sound file you desire.

You should extract the zip-file (SDL_mixer-1.2.8-win32.zip) and get six files. A readme text file, the important SDL_mixer.dll and several further .dll files. Copy all of them to the system32-folder. If you forget this and run the examples below you will get an error with exitcode = 309.

Here we go now, the complete code at once. Go through it an look if you get what the program will do when you run it.

ATTENTION: I experience a strange runtime error (216: General Protection Fault) with some curious symbols in popup window when trying this example program from IDE. The compiled program runs well though. In other projects I don’t get this behaviour. Probably it has to do with the call of MIX_VOLUMECHUNK and you may comment it out. The music and sound can’t be heard when run from IDE. You have to compile the program and execute the compiled program. This by the way isn’t needed for other projects. Not sure on the reasons for this strange behaviour here.

PROGRAM chap7;
USES CRT, SDL, SDL_MIXER;

CONST
AUDIO_FREQUENCY:INTEGER=22050;
AUDIO_FORMAT:WORD=AUDIO_S16;
AUDIO_CHANNELS:INTEGER=2;
AUDIO_CHUNKSIZE:INTEGER=4096;

VAR
userkey:CHAR;
music:pMIX_MUSIC=NIL;
sound:pMIX_CHUNK=NIL;
soundchannel:INTEGER;

BEGIN
SDL_INIT(SDL_INIT_AUDIO);


IF MIX_OPENAUDIO(AUDIO_FREQUENCY, AUDIO_FORMAT,
                 AUDIO_CHANNELS, AUDIO_CHUNKSIZE)<>0 THEN HALT;

music:=MIX_LOADMUS('In my mind.ogg');
MIX_VOLUMEMUSIC(20);

sound:=MIX_LOADWAV('dial.wav');
MIX_VOLUMECHUNK(sound,50);

writeln('Music is playing now...');
MIX_PLAYMUSIC(music,0); //-1 = infinite, 0 = once, 1 = twice,...

writeln('"s" - play sound effect');
writeln('"z" - pause sound effect');
writeln('"t" - resume sound effect');
writeln('"p" - pause music');
writeln('"o" - resume music');
writeln('"q" - quit');
REPEAT
  REPEAT
  SDL_DELAY(20);
  UNTIL KEYPRESSED;
  userkey:=READKEY;
  CASE userkey OF
    's': soundchannel:=MIX_PLAYCHANNEL(-1,sound,0);
    'z','y': MIX_PAUSE(soundchannel);
    't': MIX_RESUME(soundchannel);
    'p': MIX_PAUSEMUSIC;
    'o': MIX_RESUMEMUSIC;
  END;
UNTIL userkey='q';


MIX_HALTMUSIC;
MIX_HALTCHANNEL(soundchannel);

MIX_FREEMUSIC(music);
MIX_FREECHUNK(sound);

MIX_CLOSEAUDIO;
SDL_QUIT;
END.

Probabably you looked through the code now. Now we can go step by step through the code from top to bottom.

PROGRAM chap7;
USES CRT, SDL, SDL_MIXER;

The program is called “chap7” (quite imaginative, isn’t it?). Now we include three units. The common SDL unit already known. The CRT unit is known from chapters 3 and 4 where we needed it to check when the user pressed a key. We need it here for the very same purpose. New is the SDL_MIXER unit which is needed to allow us to use the sound subsystem in an easy way.

You may ask why we didn’t use the event handling structure we know from chapter 6 to recognize when the user presses a key instead of the keypresed command from CRT unit. Well, I think it isn’t of an advantage if you mix up the different parts of SDL for learning purposes. Furthermore this “raw” way the source code stays tighter and therefore more readable.

CONST
AUDIO_FREQUENCY:INTEGER=22050;
AUDIO_FORMAT:WORD=AUDIO_S16;
AUDIO_CHANNELS:INTEGER=2;
AUDIO_CHUNKSIZE:INTEGER=4096;

Next we define some constants which will be used to intilize the sound system. Of course you also can add the different values directly when inilizing the sound system. We need the sample frequency in Hertz (1/s) which usually is 22050 Hz or MIX_DEFAULT_FREQUENCY in games but double as high for CD quality sound (44100 Hz).

Next we have to define the audio format. It determines how the audio data is stored. Different values are given in the following table (from official SDL Mixer documentation):

audio format Description
AUDIO_U8 Unsigned 8-bit samples
AUDIO_S8 Signed 8-bit samples
AUDIO_u16LSB Unsigned 16-bit samples, in little-endian byte order
AUDIO_S16LSB Signed 16-bit samples, in little-endian byte order
AUDIO_U16MSB Unsigned 16-bit samples, in big-endian byte order
AUDIO_S16MSB Signed 16-bit samples, in big-endian byte order
AUDIO_U16 same as AUDIO_U16LSB
AUDIO_S16 same as AUDIO_S16LSB
AUDIO_U16SYS Unsigned 16-bit samples, in system byte order
AUDIO_S16SYS Signed 16-bit samples, in system byte order

Then we decide for a sound type, which means either stereo or mono. For stereo sound we choose 2 and for mono we choose 1 as value. Finally the chunksize has to be set, where 4096 bytes per sample is a default value. The lower this value the worse the quality.

VAR
userkey:CHAR;
music:pMIX_MUSIC=NIL;
sound:pMIX_CHUNK=NIL;
soundchannel:INTEGER;

Now we define some variables. First we define a CHAR varibale which later reads the keyboard input of the user. This variable is just for this purpose and has no direct connection to the sound system or SDL. Variable music which is a pMIX_MUSIC pointer is very important. It points at the loaded music file later! Same applies for sound which is a pMIX_CHUNK pointer. Both pointers are initilized as NIL pointers here. Note: Each music song is referenced by an own pMIX_MUSIC pointer and each sound effect (e.g. explosions, shots, …) is referenced by an own pMIX_CHUNK pointer.

BEGIN
SDL_INIT(SDL_INIT_AUDIO);


IF MIX_OPENAUDIO(AUDIO_FREQUENCY, AUDIO_FORMAT,
                 AUDIO_CHANNELS, AUDIO_CHUNKSIZE)<>0 THEN HALT;

music:=MIX_LOADMUS('In my mind.ogg');
MIX_VOLUMEMUSIC(20);

sound:=MIX_LOADWAV('dial.wav');
MIX_VOLUMECHUNK(sound,50);

In this demo we just want to demonstrate the music/sound abilities of SDL so we only initialize the audio subsystem by SDL_INIT_AUDIO. Similar to the initilization of the graphic system we then have to set up the audio system by the function MIX_OPENAUDIO(parameters). The four paramteters needed we already defined and discussed above. The function will return 0 on error.

We will load the music to the music pointer by MIX_LOADMUS(absolute path to music file). If the Pascal file and the music file are in the same folder you can give the music file’s name without a path (as shown in the example). The same way you load up sound files with MIX_LOADWAV function. The same function is used to load other file formats instead of .wav sound files even though the function’s name doesn’t indicate this. Both functions return NIL on error (which we don’t check in the example though). The functions MIX_VOLUMEMUSIC(integer) and MIX_VOLUMECHUNK(sound pointer, integer) set the volume for the music sound file. Note: If you have more than one music and sound file you have to set the volume for each sound file individually but the music volume is set for all music files together. The volume values are integers between 0 (silence) and 128 (highest volume). The constant MIX_MAX_VOLUME corresponds to 128.

writeln('Music is playing now...');
MIX_PLAYMUSIC(music,0); //-1 = infinite, 0 = once, 1 = twice,...

writeln('"s" - play sound effect');
writeln('"z" - pause sound effect');
writeln('"t" - resume sound effect');
writeln('"p" - pause music');
writeln('"o" - resume music');
writeln('"q" - quit');
REPEAT
  REPEAT
  SDL_DELAY(20);
  UNTIL KEYPRESSED;
  userkey:=READKEY;
  CASE userkey OF
    's': soundchannel:=MIX_PLAYCHANNEL(-1,sound,0);
    'z','y': MIX_PAUSE(soundchannel);
    't': MIX_RESUME(soundchannel);
    'p': MIX_PAUSEMUSIC;
    'o': MIX_RESUMEMUSIC;
  END;
UNTIL userkey='q';

The writeln commands for displaying text at the shell should be known. The MIX_PLAYMUSIC(music pointer, integer) is used to play music files. Therefore we give the music pointer which we called “music” and we give the number of loops this music file should be played. As the comment states -1 means the music is replayed infinitly, 0 means the music is played once, 1 means the music is played twice and so on. The function returns -1 on error and 0 on success (which we don’t check in the example).

Similarly sounds are played. Contrary to music which has its own seperated channel, sounds have up to eight channels (channel numbers 0 to 7) where they can be played at. This means you can have up to eight sounds that play at the same time and can be heard at the same time! As for music files you have to say which sound you want to play and how often but additionally you have to say at which channel you want it to play. The function is MIX_PLAYCHANNEL(paramters) which returns the channel number the sound is played on or -1 on error. The first parameter is the channel you want the music to be played. -1 means the first free channel will be chosen automatically. What will happen when all channels are used but a further sound is going to be played? One playing sound will be stopped and the new sound will be played on this channel. The next two parameters are the same as for music, the sound pointer and the integer how often the sound will be played.

The repeat-until loops, the commands KEYPRESSED, READKEY and the variable userkey aren’t SDL specific but common Pascal commands and they will hold the user in the loop until he decides to leave it by pressing the “q” button. This will not be explained here in detail.

The different cases in the case block show the usage of playing a sound (which is discussed already) and different functions to pause/resume music and sounds. The procedures MIX_PAUSEMUSIC and MIX_RESUMEMUSIC pause and resume the actively playing music. Similar for sounds but you actually don’t pause/resume the sounds themselves but the channels they are played on. This channel we don’t know because we said MIX_PLAYCHANNEL should find the first free channel automatically but we returned the channel number in variable “soundchannel”. MIX_PAUSE(channel number) and MIX_RESUME(channel number) will pause and resume the desired channel when using “soundchannel” as parameter.

MIX_HALTMUSIC;
MIX_HALTCHANNEL(soundchannel);

MIX_FREEMUSIC(music);
MIX_FREECHUNK(sound);

MIX_CLOSEAUDIO;
SDL_QUIT;
END.

The commands MIX_HALTMUSIC and MIX_HALTCHANNEL(channel number) are used to stop playing the music/sound immediatly. As for surfaces the msuic/sound files have to be freed when they are not used anymore. The functions MIX_FREEMUSIC(music pointer) and MIX_FREECHUNK(sound pointer) are used for this. Finally the audiosystem and SDL have to be shut down by MIX_CLOSEAUDIO and SDL_QUIT.

This file contains the source code: chap7.pas (right click and “save as”)
This file is the executable: chap7.exe (right click and “save as”)