I have a problem with SDL. As you see, I have 3 files: include.h, map.h, source.cpp.
In include.h I included all libraries.
In map.h I wrote a class: getTileID reads a picture and slices it to tiles, getTilePosition reads .txt file, drawMap function makes a surface assigning sliced surface tiles to id values from text file.
Then, in source.cpp I initiliaze SDL, SetVideoMode, and create a class A. After that, I call class A functions. After calling it, I A.mapSurface[0] variable to screen and flip it.
Nothing happens. Screen loads, and, as I think it flips with empty surface, but it needs to display mapSurface variable.
Please help.
//include.h
//----------------------------------------------------------------------------------------------------
#include "headers\SDL.h"
#include "headers\SDL_image.h"
//----------------------------------------------------------------------------------------------------
#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_image.lib")
//----------------------------------------------------------------------------------------------------
#include <string>
#include <fstream>
//----------------------------------------------------------------------------------------------------
#include "map.h"
-
//map.h
//----------------------------------------------------------------------------------------------------
#pragma once
//#include "include.h"
//----------------------------------------------------------------------------------------------------
class map
{
public:
//----------------------------------------------------------------------------------------------------
map(void);
~map(void);
void getTileID(const char* mapFile);
void getTilePosition(std::string mapFile);
void drawMap();
//----------------------------------------------------------------------------------------------------
static const short int TILEMAP_WIDTH = 24;
static const short int TILEMAP_HEIGHT = 24;
static const short int TILE_WIDTH = 32;
static const short int TILE_HEIGHT = 32;
SDL_Surface* tileID[1025];
short int mapWidth;
short int mapHeight;
short int mapID[2][500][500];
short int graphicLayer;
SDL_Surface* mapSurface[5];
};
//----------------------------------------------------------------------------------------------------
map::map(void)
{
}
map::~map(void)
{
}
//----------------------------------------------------------------------------------------------------
void map::getTileID(const char* mapFile)
{
IMG_Init(IMG_INIT_PNG);
SDL_Surface *tileMap;
tileMap = IMG_Load(mapFile);
if(tileMap == NULL)
{
exit(1);
}
for(short int i = 0; i < map::TILEMAP_WIDTH * map::TILEMAP_HEIGHT + 1; i++)
{
map::tileID[i] = (SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCALPHA, map::TILE_WIDTH, map::TILE_HEIGHT, 32, 0, 0, 0, 0));
if(map::tileID[i] == NULL)
{
exit(2);
}
}
tileID[0] = NULL;
short int id = 1;
for(short int c = 0; c < map::TILEMAP_WIDTH; c++)
{
for(short int r = 0; r < map::TILEMAP_HEIGHT; r++)
{
short int sliceX = c * map::TILE_WIDTH;
short int sliceY = r * map::TILE_HEIGHT;
SDL_Rect srcRect;
srcRect.x = sliceX;
srcRect.y = sliceY;
srcRect.w = map::TILE_WIDTH;
srcRect.h = map::TILE_HEIGHT;
SDL_Rect dstRect;
dstRect.x = 0;
dstRect.y = 0;
dstRect.h = 32;
dstRect.w = 32;
if(SDL_BlitSurface(tileMap, &srcRect, map::tileID[id], &dstRect) != NULL)
exit(3);
id++;
}
}
SDL_FreeSurface(tileMap);
IMG_Quit();
}
//----------------------------------------------------------------------------------------------------
void map::getTilePosition(std::string mapFile)
{
std::string
tag,
objType[10];
short int
l = 0;
float
objX[10],
objY[10],
objWidth[10],
objHeight[10];
std::ifstream data(mapFile);
while(!data.eof())
{
getline(data, tag);
if(tag == "[header]")
{
data.ignore(256, '=');
data >> map::mapWidth;
data.ignore(256, '=');
data >> map::mapHeight;
data.ignore(256, '\n');
}
map::graphicLayer = 0;
if(tag == "[layer]")
{
data.ignore(256, '\n');
data.ignore(256, '\n');
for(short int c = 0; c < map::mapHeight; c++)
{
for(short int r = 0; r < map::mapWidth; r++)
{
data >> map::mapID[map::graphicLayer][c][r];
}
}
map::graphicLayer++;
data.ignore(256, '\n');
}
if(tag.substr(0, 7) == "[object")
{
objType[l] = tag.substr(8, tag.size() - 9);
data.ignore(256, '\n');
data.ignore(256, '\n');
data.ignore(256, '=');
data >> objX[l] >> objY[l] >> objWidth[l] >> objHeight[l];
l++;
data.ignore(256, '\n');
}
}
data.close();
}
//----------------------------------------------------------------------------------------------------
void map::drawMap()
{
map::mapSurface[0] = (SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCALPHA, 3200 ,3200 , 32, 255, 255, 255, 0));
for(short int gl = 0; gl < map::graphicLayer; gl++)
{
for(short int c = 0; c < map::mapWidth; c++)
{
for(short int r = 0; r < map::mapHeight; r++)
{
for(short int id = 0; id < map::TILEMAP_WIDTH * map::TILEMAP_HEIGHT; id++)
{
if(map::mapID[gl][c][r] == id)
{
SDL_Rect dstRect;
dstRect.x = c * map::TILE_WIDTH;
dstRect.y = r * map::TILE_HEIGHT;
if(SDL_BlitSurface(map::tileID[id], NULL, map::mapSurface[gl], &dstRect) != NULL)
exit(4);
}
}
}
}
}
}
-
//source.cpp
//----------------------------------------------------------------------------------------------------
#include "../libraries/include.h"
//----------------------------------------------------------------------------------------------------
int main(int argc,char *argv[])
{
if(SDL_Init(SDL_INIT_EVERYTHING) != NULL)
exit(0);
SDL_Surface *screen;
screen = SDL_SetVideoMode(1024, 768, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
map A;
A.getTileID("map.png");
A.getTilePosition("map.txt");
A.drawMap();
screen = A.mapSurface[0];
SDL_Flip(screen);
SDL_Delay(1000);
SDL_FreeSurface(screen);
SDL_Quit();
return 0;
}
It looks like you are drawing your map to map::mapSurface[0] but this surface is never drawn to the screen. Blit your temporary surface to screen after drawMap or draw directly to the screen.
Related
As a school project, I've made the classic snake game using SDL2 and C++.
I've already implemented the growing, moving features for the Snake but it was required to make the movement based on a grid, but when I implemented the grid feature, the self-collision was always triggering whenever grow one part, so every time I start the game, and eat the first fruit, the snake dies.
I've been trying for a while now, from placing a delay to the adding of the tail and delaying the collision check, but to no avail, it's always colliding with itself even though it is not.
I can't see what is wrong with the self collision, I would gladly appreciate it if someone can point out what's wrong.
Snake.h
#include "GameObject.h"
#include "common.h"
#include "draw.h"
#include "Food.h"
#include "util.h"
#include <vector>
struct Segment {
int x;
int y;
Segment(int posx, int posy) {
x = posx;
y = posy;
}
};
class Snake :
public GameObject
{
public:
~Snake();
void start();
void update();
void draw();
void outOfBoundsCheck();
void move();
void addSegment(int x, int y);
void selfCollisionCheck(bool hasEaten);
void setHasMoved(bool a);
void setIsAlive(bool a);
int getX();
int getY();
int getWidth();
int getHeight();
bool getIsAlive();
bool getHasMoved();
std::vector<Segment*> const& getV() const;
private:
std::vector<Segment*> body;
SDL_Texture* headTexture;
SDL_Texture* bodyTexture;
int x;
int y;
int width;
int height;
int dx;
int dy;
int tempX;
int tempY;
bool isAlive;
bool hasMoved;
};
Snake.cpp
Snake::~Snake()
{
}
void Snake::start()
{
// Load Texture
headTexture = loadTexture("gfx/player.png");
bodyTexture = loadTexture("gfx/body.png");
tempX = 0;
tempY = 0;
x = 0;
y = 0;
dx = 0;
dy = 0;
isAlive = true;
hasMoved = false;
width = 0;
height = 0;
SDL_QueryTexture(headTexture, NULL, NULL, &width, &height);
addSegment(x, y);
}
void Snake::update()
{
std::cout << "Head" << body[0]->x << std::endl;
if (body.size() > 1) {
std::cout << "2nd Segment" << body[1]->x << std::endl;
if (body.size() > 2) {
std::cout << "3nd Segment" << body[2]->x << std::endl;
}
}
move();
outOfBoundsCheck();
}
void Snake::draw()
{
if (!isAlive) return; // Cancel the render if player dies
for (int i = 0; i < body.size(); i++) {
blit(headTexture, body[i]->x, body[i]->y);
}
}
void Snake::outOfBoundsCheck()
{
for (int i = 0; i < body.size(); i++) {
if (body[i]->x > SCREEN_WIDTH) {
body[i]->x = 0;
}
if (body[i]->x < 0) {
body[i]->x = SCREEN_WIDTH;
}
if (body[i]->y > SCREEN_HEIGHT) {
body[i]->y = 0;
}
if (body[i]->y < 0) {
body[i]->y = SCREEN_HEIGHT;
}
}
}
void Snake::move()
{
if (app.keyboard[SDL_SCANCODE_W] && dy != 5) {
dx = 0;
dy = -5;
}
if (app.keyboard[SDL_SCANCODE_A] && dx != 5) {
dx = -5;
dy = 0;
}
if (app.keyboard[SDL_SCANCODE_S] && dy != -5) {
dx = 0;
dy = 5;
}
if (app.keyboard[SDL_SCANCODE_D] && dx != -5) {
dx = 5;
dy = 0;
}
Segment* snakeHead = *(body.begin()); //Grid
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
for (int i = body.size() - 1; i > 0; i--) { //For the other parts to follow
body[i]->x = body[i - 1]->x;
body[i]->y = body[i - 1]->y;
}
}
void Snake::addSegment(int x, int y)
{
Segment* seg = new Segment(x, y );
body.push_back(seg);
}
void Snake::selfCollisionCheck(bool hasEaten) // Fail
{
Segment* head = body[0];
if (hasEaten == false) {
for (int i = 1; i < body.size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
isAlive = false;
break;
}
}
}
else {
return;
}
}
void Snake::setHasMoved(bool a)
{
hasMoved = a;
}
void Snake::setIsAlive(bool a)
{
isAlive = a;
}
int Snake::getX()
{
return x;
}
int Snake::getY()
{
return y;
}
int Snake::getWidth()
{
return width;
}
int Snake::getHeight()
{
return height;
}
bool Snake::getIsAlive()
{
return isAlive;
}
bool Snake::getHasMoved()
{
return hasMoved;
}
std::vector<Segment*> const& Snake::getV() const
{
// TODO: insert return statement here
return body;
}
GameScene.h
#include "Scene.h"
#include "GameObject.h"
#include "Snake.h"
#include "Food.h"
#include "util.h"
#include "text.h"
#include "SoundManager.h"
class GameScene : public Scene
{
public:
GameScene();
~GameScene();
void start();
void draw();
void update();
void spawnFood();
void collisionLogic();
void selfCollision();
void despawnFood(Food* food);
private:
Snake* snake;
Food* food;
int points;
std::vector<Food*> spawnedFood;
};
GameScene.cpp
#include "GameScene.h"
GameScene::GameScene()
{
// Register and add game objects on constructor
snake = new Snake();
this->addGameObject(snake);
points = 0;
}
GameScene::~GameScene()
{
delete snake;
delete food;
}
void GameScene::start()
{
Scene::start();
// Initialize any scene logic here
initFonts();
spawnFood();
}
void GameScene::draw()
{
Scene::draw();
drawText(110, 20, 255, 255, 255, TEXT_CENTER, "POINTS: %03d", points);
if (snake->getIsAlive() == false) {
drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 255, 255, 255, TEXT_CENTER, "GAME OVER!");
}
}
void GameScene::update()
{
Scene::update();
if (spawnedFood.size() == 0 && spawnedFood.size() != 1) {
spawnFood();
}
collisionLogic();
selfCollision();
}
void GameScene::spawnFood()
{
int random = rand() % 720;
if (random % 25 != 0) {
random = rand() % 720;
}
else {
Food* food = new Food();
this->addGameObject(food);
food->setPosition(rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT);
spawnedFood.push_back(food);
}
}
void GameScene::collisionLogic()
{
Segment* head = snake->getV()[0];
std::vector<Segment*> snakeBody = snake->getV();
for (int i = 0; i < objects.size(); i++) {
Food* food = dynamic_cast<Food*>(objects[i]);
if (food != NULL) {
int collision = checkCollision(
head->x, head->y, snake->getWidth(), snake->getHeight(),
food->getX(), food->getY(), food->getWidth(), food->getHeight()
);
if (collision == 1) {
despawnFood(food);
snake->addSegment(snakeBody[snakeBody.size() - 1]->x, snakeBody[snakeBody.size() - 1]->y); //Adds a part to the snake
points++;
break;
}
}
}
}
void GameScene::selfCollision()
{
std::vector<Segment*> body = snake->getV();
Segment* head = snake->getV()[0];
for (int i = 1; i < snake->getV().size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
snake->setIsAlive(false);
break;
}
}
}
void GameScene::despawnFood(Food* food)
{
int index = -1;
for (int i = 0; i < spawnedFood.size(); i++) {
if (food == spawnedFood[i]) {
index = i;
break;
}
}
if (index != -1) {
spawnedFood.erase(spawnedFood.begin() + index);
delete food;
}
}
It seems that I had some logical errors when it comes to the grid movement because when I re-coded everything and changed the grid movement into cell based instead of using modulo condition by dividing the screen width and height to the pixel size of my snake and using that as the coordinates for my movement, everything went back to normal and the collision bug disappeared.
Instead of doing this for the grid movement
Old Grid Movement Code
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
I defined this as a permanent value in my defs.h
defs.h
#define CELL_SIZE 25 // Size of the segment
#define CELL_WIDTH SCREEN_WIDTH / CELL_SIZE
#define CELL_HEIGHT SCREEN_HEIGHT / CELL_SIZE
After that, since I'm still going to render the picture with the default resolution, I multiplied CELL_SIZE to the dest variable of my blit function
draw.cpp
void blit(SDL_Texture* texture, int x, int y)
{
SDL_Rect dest;
dest.x = x * CELL_SIZE;
dest.y = y * CELL_SIZE;
SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(app.renderer, texture, NULL, &dest);
}
This results to the snake and any other thing that I'm going to render to follow a grid and by assigning the x and y values with the CELL_WIDTH and CELL_HEIGHT as substitution to the resolution, I accomplished the grid movement with no conflict with my collision check.
I'm really new to c++ programming with SDL and I've got a really strange problem when running my program: SDL crashes when it arrives to SDL_CreateTextureFromSurface and then I couldn't even close the window using Task Manager! I'm very confused because the code worked so well and I didn't change anything before having this problem! Tried even to use debugger but it failed to locate the error:
After googling for hours and hours, didn't find any satisfying result. Here's my code:
#include <SDL.h>
#include <SDL_ttf.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <vector>
const int WIN_W = 1000;
const int WIN_H = 550;
const int CEN_W = 511;
const int CEN_H = 511;
const int CEN_X = (WIN_W - CEN_W) / 2;
const int CEN_Y = (WIN_H - CEN_H) / 2;
const int SEP_W = 3;
const int MAX_X = 4;
const int MAX_Y = 4;
const int nbImages = 11;
int numbers[MAX_X][MAX_Y];
int winNumbers[MAX_X][MAX_Y];
TTF_Font* gameNbFont = NULL;
TTF_Font* textFont = NULL;
TTF_Font* pixelFont = NULL;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Texture* numbersTextures[MAX_X * MAX_Y - 1];
SDL_Texture* bgImage = NULL;
SDL_Texture* bgImages[nbImages];
SDL_Rect* numbersContsRectsPos[MAX_X][MAX_Y];
SDL_Rect* numbersContsRectsCrops[MAX_X * MAX_Y - 1];
SDL_Rect* restartButtonRect = NULL;
SDL_Texture* timeStringTexture = NULL;
SDL_Texture* timeIntTexture = NULL;
SDL_Texture* movesStringTexture = NULL;
SDL_Texture* movesIntTexture = NULL;
SDL_Texture* restartButtonTexture;
enum texturesSizesEnum{
TIME_STRING,
TIME_INT,
MOVES_STRING,
MOVES_INT,
TEXTURES_SIZES_TOTAL
};
std::pair<int, int> texturesSizes[TEXTURES_SIZES_TOTAL];
std::pair<int, int> numbersTexturesSizes[MAX_X * MAX_Y - 1];
int currentTime = -1;
int nbMoves = -1;
int timeFraction = 0;
bool stop;
void updateTime(){
if (currentTime == 999){
return;
}
currentTime ++;
std::ostringstream sm;
sm << currentTime;
std::string time = std::string(3-sm.str().length(), '0') + sm.str();
SDL_Surface* timeIntSurface = TTF_RenderText_Solid(pixelFont, time.c_str(), SDL_Color({0XFF, 0XFF, 0XFF}));
timeIntTexture = SDL_CreateTextureFromSurface(renderer, timeIntSurface);
texturesSizes[TIME_INT] = std::make_pair(timeIntSurface->w, timeIntSurface->h);
}
void updateMoves(){
if (nbMoves == 999){
return;
}
nbMoves ++;
std::ostringstream sm;
sm << nbMoves;
std::string moves = std::string(3-sm.str().length(), '0') + sm.str();
SDL_Surface* movesIntSurface = TTF_RenderText_Solid(pixelFont, moves.c_str(), SDL_Color({0XFF, 0XFF, 0XFF}));
movesIntTexture = SDL_CreateTextureFromSurface(renderer, movesIntSurface);
texturesSizes[MOVES_INT] = std::make_pair(movesIntSurface->w, movesIntSurface->h);
}
bool initTextures(){
printf("started\n");
bool success=false;
gameNbFont = TTF_OpenFont("data/fonts/gothic.ttf", 40);
if (gameNbFont == NULL){
printf("Couldn't load data/fonts/gothic.ttf font! TTF_Error: %s\n", TTF_GetError());
}
else{
printf("breakpoint1\n");
for (int i=1; i<MAX_X * MAX_Y; i++){
printf("breakpoint1.%i.1\n", i);
std::ostringstream stm;
printf("breakpoint1.%i.2\n", i);
stm << i;
printf("breakpoint1.%i.3\n", i);
SDL_Surface* number = TTF_RenderText_Solid(gameNbFont, stm.str().c_str(), SDL_Color({0X4E, 0XCD, 0XC4}));
printf("breakpoint1.%i.4\n", i); //program crashes after printing this :(
numbersTextures[i-1] = SDL_CreateTextureFromSurface(renderer, number);
printf("breakpoint1.%i.5\n", i);
SDL_FreeSurface(number);
printf("breakpoint1.%i.6\n", i);
numbersTexturesSizes[i-1] = std::make_pair(number->w, number->h);
}
printf("breakpoint2\n");
textFont = TTF_OpenFont("data/fonts/PlayfairDisplay.ttf", 50);
if (textFont == NULL){
printf("Couldn't load data/fonts/PlayfairDisplay.ttf font! TTF_Error: %s", TTF_GetError());
}
else{
printf("breakpoint3");
SDL_Surface* timeStringSurface = TTF_RenderText_Solid(textFont, "TIME:", SDL_Color({0XFF, 0xFF, 0XFF}));
timeStringTexture = SDL_CreateTextureFromSurface(renderer, timeStringSurface);
texturesSizes[TIME_STRING] = std::make_pair(timeStringSurface->w, timeStringSurface->h);
SDL_Surface* movesStringSurface = TTF_RenderText_Solid(textFont, "MOVES:", SDL_Color({0XFF, 0xFF, 0XFF}));
movesStringTexture = SDL_CreateTextureFromSurface(renderer, movesStringSurface);
texturesSizes[MOVES_STRING] = std::make_pair(movesStringSurface->w, movesStringSurface->h);
pixelFont = TTF_OpenFont("data/fonts/pixels.ttf", 40);
if (pixelFont == NULL){
printf("Couldn't load data/fonts/pixels.ttf font! TTF_Error: %s", TTF_GetError());
}
else{
bool loadedAllImages = true;
for (int i=1; i<=nbImages; i++){
std::ostringstream sm;
sm << "data/images/examples/image" << i << ".bmp";
SDL_Surface* image = SDL_LoadBMP(sm.str().c_str());
if (image == NULL){
printf("Couldn't load %s! SDL_Error: %s", sm.str().c_str(), SDL_GetError());
loadedAllImages = false;
break;
}
else{
image = SDL_ConvertSurface(image, image->format, 0);
SDL_Surface* scaledImage = SDL_CreateRGBSurface(0, CEN_W, CEN_H, 32, 0, 0, 0, 0);
SDL_Rect* stretchRect = new SDL_Rect({0, 0, CEN_W, CEN_H});
SDL_BlitScaled(image, NULL, scaledImage, stretchRect);
bgImages[i] = SDL_CreateTextureFromSurface(renderer, scaledImage);
}
}
if (loadedAllImages){
bgImage = bgImages[rand() % nbImages + 1];
for (int i=0; i<MAX_X * MAX_Y - 1; i++){
numbersContsRectsCrops[i] = new SDL_Rect({
numbersContsRectsPos[0][0]->w * (i % MAX_Y),
numbersContsRectsPos[0][0]->h * (i / MAX_Y),
numbersContsRectsPos[0][0]->w,
numbersContsRectsPos[0][0]->h});
}
SDL_Surface* restartButton = SDL_LoadBMP("data/images/restart.bmp");
if (restartButton == NULL){
printf("Couldn't load data/images/restart.bmp! SDL_Error: %s)", SDL_GetError());
}
else{
restartButton = SDL_ConvertSurface(restartButton, restartButton->format, 0);
restartButtonTexture = SDL_CreateTextureFromSurface(renderer, restartButton);
restartButtonRect = new SDL_Rect({(CEN_X + CEN_W + WIN_W - restartButton->w) / 2,
CEN_Y,
restartButton->w,
restartButton->h});
updateTime();
updateMoves();
success = true;
}
}
}
}
}
return success;
}
void shuffle(std::vector<int> *arr){
srand(time(0));
std::vector<int> workspace;
for (int i=0; i<MAX_X*MAX_Y-1; i++){
workspace.push_back(i);
}
for (int i=0; i<MAX_X*MAX_Y-1; i++){
int rv = rand() % (MAX_X * MAX_Y - 1 - i);
arr->push_back(workspace[rv]);
workspace.erase(workspace.begin() + rv);
}
}
void initNumbers(){
std::vector<int> shuffledNumbers;
shuffle(&shuffledNumbers);
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
numbers[x][y] = shuffledNumbers[x + y * MAX_X];
}
}
numbers[MAX_X-1][MAX_Y-1] = -1;
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
winNumbers[x][y] = x + y * MAX_X;
numbersContsRectsPos[x][y] = new SDL_Rect({CEN_X + SEP_W + ((CEN_W - SEP_W) / MAX_X) * x,
CEN_Y + SEP_W + ((CEN_H - SEP_W) / MAX_Y) * y,
(CEN_W - SEP_W) / MAX_X - SEP_W,
(CEN_H - SEP_W) / MAX_Y - SEP_W});
}
}
winNumbers[MAX_X][MAX_Y] = -1;
}
bool init(){
bool success = false;
if (SDL_Init(SDL_INIT_EVERYTHING) < 0){
printf("SDL couldn't init! SDL_Error: %s\n", SDL_GetError());
}
else if (TTF_Init() < 0){
printf("TTF couldn't init! TTF_Error: %s\n", TTF_GetError());
}
else{
window = SDL_CreateWindow("Sliding Puzzle", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIN_W, WIN_H, SDL_WINDOW_SHOWN);
if (window==NULL){
printf("SDL Window couldn't be created! SDL_Error: %s\n", SDL_GetError());
}
else{
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL){
printf("SDL Renderer couldn't be created! SDL_Error: %s\n", SDL_GetError());
}
else{
SDL_Surface* icon = SDL_LoadBMP("data/images/icon.bmp");
if (icon == NULL){
printf("Couldn't load data/images/icon.bmp! SDL_Error: %s\n", SDL_GetError());
}
else{
SDL_SetWindowIcon(window, icon);
initNumbers();
success = initTextures();
}
}
}
}
return success;
}
void mouseClick(int mouseX, int mouseY){
int rx1 = restartButtonRect->x;
int ry1 = restartButtonRect->y;
int rx2 = restartButtonRect->w + rx1;
int ry2 = restartButtonRect->h + ry1;
if (mouseX >= rx1 && mouseX <= rx2 &&
mouseY >= ry1 && mouseY <= ry2){
std::vector<int> shuffledNumbers;
shuffle(&shuffledNumbers);
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
numbers[x][y] = shuffledNumbers[x + y * MAX_X];
}
}
numbers[MAX_X-1][MAX_Y-1] = -1;
currentTime = -1;
updateTime();
timeFraction = 0;
nbMoves = 0;
bgImage = bgImages[rand() % nbImages + 1];
}
else{
std::pair<int, int> collCoor = {-1, -1};
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
if (numbers[x][y] != -1){
int nbx1 = numbersContsRectsPos[x][y]->x;
int nby1 = numbersContsRectsPos[x][y]->y;
int nbx2 = numbersContsRectsPos[x][y]->w + nbx1;
int nby2 = numbersContsRectsPos[x][y]->h + nby1;
if (mouseX >= nbx1 && mouseX <= nbx2 &&
mouseY >= nby1 && mouseY <= nby2){
collCoor.first = x;
collCoor.second = y;
}
}
}
}
if (collCoor.first != -1){
std::pair<int, int> swapCoor = {-1, -1};
for (int x=-1; x<=1; x+=2){
if (collCoor.first + x >= 0 && collCoor.first + x <MAX_X){
if (numbers[collCoor.first + x][collCoor.second] == -1){
swapCoor.first = collCoor.first + x;
swapCoor.second = collCoor.second;
}
}
}
for (int y=-1; y<=1; y+=2){
if (collCoor.second + y >= 0 && collCoor.second + y <MAX_Y){
if (numbers[collCoor.first][collCoor.second + y] == -1){
swapCoor.first = collCoor.first;
swapCoor.second = collCoor.second + y;
}
}
}
if (swapCoor.first != -1){
std::swap(numbers[collCoor.first][collCoor.second], numbers[swapCoor.first][swapCoor.second]);
updateMoves();
}
}
}
}
int main(int argc, char **argv){
if (init()){
bool quit = false;
stop = false;
SDL_Event e;
int mouseX, mouseY;
while (!quit){
while (SDL_PollEvent(&e) != 0){
switch(e.type){
case SDL_QUIT:
quit = true;
break;
case SDL_MOUSEBUTTONDOWN:
SDL_GetMouseState(&mouseX, &mouseY);
mouseClick(mouseX, mouseY);
break;
}
}
SDL_SetRenderDrawColor(renderer, 0XFF, 0X6B, 0X6B, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_Rect* fillRect = new SDL_Rect({CEN_X, CEN_Y, CEN_W, CEN_H});
SDL_SetRenderDrawColor(renderer, 0XFF, 0XFF, 0XFF, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(renderer, fillRect);
SDL_SetRenderDrawColor(renderer, 0XFF, 0XFF, 0XFF, SDL_ALPHA_OPAQUE);
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
if (numbers[x][y] != -1){
//SDL_RenderFillRect(renderer, numbersContsRectsPos[x][y]);
SDL_RenderCopy(renderer, bgImage,
numbersContsRectsCrops[numbers[x][y]],
numbersContsRectsPos[x][y]);
}
}
}
SDL_Rect* numberRect;
for (int x=0; x<MAX_X; x++){
for (int y=0; y<MAX_Y; y++){
if (numbers[x][y] != -1){
numberRect = new SDL_Rect({numbersContsRectsPos[x][y]->x + 5,
numbersContsRectsPos[x][y]->y + 5,
numbersTexturesSizes[numbers[x][y]].first,
numbersTexturesSizes[numbers[x][y]].second});
SDL_RenderCopy(renderer, numbersTextures[numbers[x][y]], NULL, numberRect);
}
}
}
SDL_Rect* posRect;
posRect = new SDL_Rect({10, 10, texturesSizes[TIME_STRING].first, texturesSizes[TIME_STRING].second});
SDL_RenderCopy(renderer, timeStringTexture, NULL, posRect);
posRect = new SDL_Rect({10, posRect->y + texturesSizes[TIME_STRING].second + 5,
texturesSizes[TIME_INT].first, texturesSizes[TIME_INT].second});
SDL_RenderCopy(renderer, timeIntTexture, NULL, posRect);
posRect = new SDL_Rect({10, posRect->y + texturesSizes[TIME_INT].second + 15,
texturesSizes[MOVES_STRING].first, texturesSizes[MOVES_STRING].second});
SDL_RenderCopy(renderer, movesStringTexture, NULL, posRect);
posRect = new SDL_Rect({10, posRect->y + texturesSizes[MOVES_STRING].second + 5,
texturesSizes[MOVES_INT].first, texturesSizes[MOVES_INT].second});
SDL_RenderCopy(renderer, movesIntTexture, NULL, posRect);
SDL_RenderCopy(renderer, restartButtonTexture, NULL, restartButtonRect);
SDL_RenderPresent(renderer);
SDL_ShowWindow(window);
SDL_Delay(10);
if (!stop){
timeFraction += 10;
if (timeFraction == 1000){
updateTime();
timeFraction = 0;
}
if (winNumbers == numbers){
stop = true;
}
}
}
}
SDL_Quit();
return 0;
}
I'm using mingw compiler with code::blocks (Sorry for my bad English)
Just repaired it. The error was very simple: I was trying to access winNumbers wich has a length of MAX_X with index MAX_X (it should be MAX_X - 1). However, I didn't know why the debugger did not detect the error.
I´m using allegro 4.4.2 on Visual Studio 2012 for a school project. Allegro is installed and working, and I'm trying to get it to load a map.txt file which is located in the project folder. When debugging, allegro freezes and becomes incredibly slow and throws an unhandled exception, violation access code at me.
This is Map.h:
#include <allegro.h>
#include "Global.h"
#include <fstream>
using namespace std;
class Map
{
public:
Map();
~Map();
void Init();
void Update();
void Draw(BITMAP *Buffer);
void LoadMap (const char*filename);
private:
int loadCounterX;
int loadCounterY;
int mapSizeX;
int mapSizeY;
int MapFile[20][15];
};
And this is Map.cpp:
#include "Map.h"
Map::Map()
{
}
Map::~Map()
{
}
void Map::Init()
{
loadCounterX = loadCounterY = 0;
Map::LoadMap("map1.txt");
}
void Map::Update()
{
}
void Map::Draw(BITMAP *Buffer)
{
for (int i = 0; 1 < mapSizeX; i++)
{
for (int j = 0; j < mapSizeY; j++)
{
if (MapFile[i][j] == 1)
{
rectfill(Buffer, i*BlockSize, j*BlockSize, i*BlockSize + BlockSize, j*BlockSize + BlockSize, makecol(0, 255, 255));
}
else if (MapFile[i][j] == 2)
{
rectfill(Buffer, i*BlockSize, j*BlockSize, i*BlockSize + BlockSize, j*BlockSize + BlockSize, makecol(0, 255, 0));
}
}
}
}
void Map::LoadMap(const char*filename)
{
ifstream openfile (filename);
if (openfile.is_open())
{
openfile >> mapSizeX >> mapSizeY;
while (!openfile.eof())
{
openfile >> MapFile[loadCounterX][loadCounterY];
loadCounterX ++;
if (loadCounterX >= mapSizeX)
{
loadCounterX = 0;
loadCounterY ++;
}
}
loadCounterX = loadCounterY = 0;
} //File is opened
else
{
allegro_message ("Map File couldn't be found");
}
}
and here is my main file:
#include <allegro.h>
#include "Player.h"
#include "Global.h"
#include "Camera.h"
#include "Map.h"
using namespace std;
volatile int counter = 0;
void Increment ()
{
counter ++;
}
int main (void)
{
allegro_init();
install_keyboard();
install_mouse();
install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, "A");
set_color_depth(32);
set_gfx_mode (GFX_AUTODETECT_WINDOWED, ScreenWidth, ScreenHeight, 0, 0);
LOCK_VARIABLE (counter);
LOCK_FUNCTION (Increment);
install_int_ex (Increment, BPS_TO_TIMER(100));
BITMAP *Buffer = create_bitmap (6000, ScreenHeight);
bool done = false;
Player player;
Camera camera;
Map map;
player.Init();
camera.Init();
map.Init();
while (!done)
{
while (counter > 0)
{
//Input
if (key[KEY_ESC])
done = true;
//Update
map.Update();
player.Update();
camera.Update(player.x, player.y);
counter --;
}
//Draw
map.Draw(Buffer);
player.Draw(Buffer);
camera.Draw(Buffer);
clear_bitmap(Buffer);
}
return 0;
}
END_OF_MAIN();
It crashes at this line
if (MapFile[i][j] == 1)
everytime. All of the variables shown in "Autos" in Visual Studio turn red; "MapFile" "MapFile[i]" (which I don't understand.. shouldn't this just be "i"?) "j" "mapSizeY" and "this" However when I expand the "MapFile", the first 20 blocks are filled out correctly as they are in my map.txt file.
I'm completely lost and have no idea what to do... any help is greatly appreciated!
In void Map::Draw(BITMAP *Buffer) you use 1 < mapSizeX instead of i < mapSizeX.
You might also want to prevent calling Map::Draw when the Map::LoadMap wasn't called before.
I have been working in a zombie arcade game, I am experimenting an strange problem. I created a class called GameObject and a class called Human that inherits from GameOBject, then I declared two more called; Zombie and Player. The problem is that when I create two or more characters (instances) of this four classes the game stops working, I detected that in the classes Zombie and Player the problem only occurs when I call the method update (only these classes use it). This method is supposed to control the activities of each character.
The graphics and animated sprites are managed separately from the character, they (the characters) only call some methods to control the sprite update, those orders are processed when updating the screen.
I just want you to tell me what is happening, I think the problem is in the update method of the classes Zombie and Player.
The project is divided in different files.
I don't think this matters but I am using CodeBlocks and tdm-gcc.
main.cpp:
#include "game_classes.h"
bool working = true, redraw = true;
int main(){
init_game_classes();
Player player(200, 200);
std::vector<Zombie *> zombie_list;
for(unsigned int i = 0; i < 2; i++){
Zombie *zombie = new Zombie(i * 100, 12, &player);
zombie_list.push_back(zombie);
}
while(working){
input();
if(event.type == ALLEGRO_EVENT_TIMER){//main loop
player.update();
for(unsigned int i = 0; i < zombie_list.size(); i++){
zombie_list[i]->update();
}
redraw = true;
}
else if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE){
working = false;
}
if(redraw and al_is_event_queue_empty(event_queue)){//updating display
update_animated_models();
blit_models();
update_display();
redraw = false;
}
}
for(unsigned int i = 0; i < zombie_list.size(); i++)
delete zombie_list[i];
quit();
return 0;
}
render.h:
#include <vector>
#include <stdio.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
int global_ticks_per_frame = 0;
ALLEGRO_BITMAP *map_background = NULL;
class Model;
std::vector<Model *> model_list;
class Model{
protected:
int x = 0, y = 0, width, height;
int source_x = 0, source_y = 0, source_width, source_height;
public:
ALLEGRO_BITMAP *sheet = NULL;
bool hide = false;
int flags = 0;
Model(const char *path, int width, int height){
this->sheet = al_load_bitmap(path);
al_convert_mask_to_alpha(this->sheet, al_map_rgb(255, 0, 255));
this->source_width = al_get_bitmap_width(this->sheet);
this->source_height = al_get_bitmap_height(this->sheet);
this->width = width;
this->height = height;
model_list.insert(model_list.begin(), this);
}
Model(ALLEGRO_BITMAP *image, int width, int height){
this->sheet = image;
al_convert_mask_to_alpha(this->sheet, al_map_rgb(255, 0, 255));
this->source_width = al_get_bitmap_width(this->sheet);
this->source_height = al_get_bitmap_height(this->sheet);
this->width = width;
this->height = height;
model_list.insert(model_list.begin(), this);
}
~Model(){
al_destroy_bitmap(this->sheet);
model_list.erase(model_list.begin() + this->index());
}
void show(){
if(not this->hide){
al_draw_scaled_bitmap(this->sheet, this->source_x, this->source_y, this->source_width, this->source_height, x, y, width, height, this->flags);
}
}
void set_x(int x){
this->x = x;
}
unsigned int index(){
for(unsigned int i = 0; i < model_list.size(); i++){
if(model_list[i] == this)
return i;
}
}
bool set_y(int y){
this->y = y;
model_list.erase(model_list.begin() + this->index());
int this_relative_y = this->y + this->height;
unsigned int i = 0;
while(i < model_list.size()){
int from_list_relative_y = model_list[i]->y + model_list[i]->height;
if(this_relative_y < from_list_relative_y){
model_list.insert(model_list.begin() + i, this);
return false;
}
i += 1;
}
model_list.push_back(this);
return false;
}
int get_y(){
return this->y;
}
int get_x(){
return this->x;
}
unsigned int get_width(){
return this->width;
}
unsigned int get_height(){
return this->height;
}
};
void blit_models(){
for(unsigned int i = 0; i < model_list.size(); i++)
model_list[i]->show();
}
class AnimatedModel;
std::vector<AnimatedModel *> animated_model_list;
class AnimatedModel : public Model{
private:
unsigned int ticks_per_frame, ticks_counter = 0;
unsigned int current_frame = 0, frame_count;
public:
bool stop = false;
void set_speed(unsigned int new_speed){
this->ticks_per_frame = new_speed;
}
AnimatedModel(const char *path, unsigned int frames, unsigned int ticks_per_frame, int width, int height) : Model(path, width, height){
this->ticks_per_frame = ticks_per_frame;
this->frame_count = frames;
this->source_width /= frames;
animated_model_list.push_back(this);
}
AnimatedModel(ALLEGRO_BITMAP *image, unsigned int frames, unsigned int ticks_per_frame, int width, int height) : Model(image, width, height){
this->ticks_per_frame = ticks_per_frame;
this->frame_count = frames;
this->source_width /= frames;
animated_model_list.push_back(this);
}
~AnimatedModel(){
for(unsigned int i = 0; i < animated_model_list.size(); i++){
if(animated_model_list[i] == this){
animated_model_list.erase(animated_model_list.begin() + i);
}
}
}
void fix_sheet_looking(){
if(not this->stop)
this->source_x = this->current_frame*this->source_width;
}
void play(){
if(this->ticks_counter >= this->ticks_per_frame + global_ticks_per_frame){
this->current_frame += 1;
if(this->current_frame >= this->frame_count)
this->current_frame = 0;
this->ticks_counter = 0;
}
else{
this->ticks_counter += 1;
}
}
void update(){
if(not this->stop){
this->play();
this->fix_sheet_looking();
}
}
void set_frame(unsigned int i){
this->current_frame = i;
}
unsigned int get_frame(){
return this->current_frame;
}
};
void update_animated_models(){
for(unsigned int i = 0; i < animated_model_list.size(); i++)
animated_model_list[i]->update();
}
game_classes.h:
#include "render.h"
#include "touch.h"
#include "window_control.h"
#include <string>
#include <iostream>
ALLEGRO_BITMAP *zombie_sprite = NULL;
ALLEGRO_BITMAP *human_sprite = NULL;
void init_game_classes(){
init_window_control();
zombie_sprite = al_load_bitmap("textures/zombie/sprite.png");
human_sprite = al_load_bitmap("textures/human/sprite.png");
}
int control_key_up = ALLEGRO_KEY_UP, control_key_down = ALLEGRO_KEY_DOWN, control_key_right = ALLEGRO_KEY_RIGHT, control_key_left = ALLEGRO_KEY_LEFT;
int control_key_run = ALLEGRO_KEY_Z;
HitBoxList character_body_reg;
HitBoxList item_body_reg;
class GameObjec{
protected:
unsigned int walking_speed;
HitBoxList *last_coll_test = NULL;
int last_x, last_y;
int body_high;
int left_distance;
public:
HitBox *body = NULL;
AnimatedModel *sprite = NULL;
void set_x(int x){
this->sprite->set_x(x);
this->last_x = this->body->x;
this->body->x = x + this->left_distance;
}
void set_y(int y){
this->sprite->set_y(y);
this->last_y = this->body->y;
this->body->y = y + this->body_high;
}
int get_x(){
return this->sprite->get_x();
}
int get_y(){
return this->sprite->get_y();
}
void slide_x(short int direction){
character_body_reg.pop(this->body);
this->last_coll_test = this->body->slide_x(this->walking_speed*direction, &character_body_reg);
character_body_reg.push(this->body);
this->set_x(this->body->x - this->left_distance);
}
void slide_y(short int direction){
character_body_reg.pop(this->body);
this->last_coll_test = this->body->slide_y(this->walking_speed*direction, &character_body_reg);
character_body_reg.push(this->body);
this->set_y(this->body->y - this->body_high);
}
void show_hitbox(){
al_draw_rectangle(this->body->x, this->body->y, this->body->x + this->body->width, this->body->y + this->body->height, al_map_rgb(255, 0, 0), 1);
}
GameObjec(int x, int y, unsigned int walking_speed, const char *sprite_image_path, int frames, int ticks_per_frame, int sprite_width, int sprite_height, int body_high, int body_len){
this->walking_speed = walking_speed;
this->body_high = body_high;
this->sprite = new AnimatedModel(sprite_image_path, frames, ticks_per_frame, sprite_width, sprite_height);
this->left_distance = (this->sprite->get_width() - body_len)/2;
this->body = new HitBox;
this->sprite->set_x(x);
this->sprite->set_y(y);
this->body->width = body_len;
this->body->height = this->sprite->get_height() - this->body_high;
this->body->x = x + left_distance;
this->body->y = y + this->body_high;
this->last_x = this->body->x;
this->last_y = this->body->y;
character_body_reg.push(this->body);
}
~GameObjec(){
delete this->sprite;
character_body_reg.pop(this->body);
delete this->body;
}
void draw_hitbox(){
al_draw_rectangle(this->body->x, this->body->y, this->body->x + this->body->width, this->body->y + this->body->height, al_map_rgb(255, 0, 0), 0);
}
};
class Human : public GameObjec{
protected:
bool walking = false;
public:
Human(int x, int y, const char *sprite_image_path) : GameObjec(x, y, 2, sprite_image_path, 2, 7, 64, 80, 60, 32){}
~Human(){}
void walk_down(){
this->slide_y(1);
this->sprite->stop = false;
this->walking = true;
}
void walk_up(){
this->slide_y(-1);
this->sprite->stop = false;
this->walking = true;
}
void walk_right(){
this->slide_x(1);
this->sprite->stop = false;
this->sprite->flags = 0;
this->walking = true;
}
void walk_left(){
this->slide_x(-1);
this->sprite->stop = false;
this->sprite->flags = ALLEGRO_FLIP_HORIZONTAL;
this->walking = true;
}
};
class Player : public Human{
public:
Player(int x, int y) : Human(x, y, "textures/human/sprite.png"){
}
void control(){
if(get_key(control_key_down))
this->walk_down();
else if(get_key(control_key_up))
this->walk_up();
if(get_key(control_key_right))
this->walk_right();
else if(get_key(control_key_left))
this->walk_left();
if(not this->walking){
this->sprite->set_frame(0);
this->sprite->fix_sheet_looking();
this->sprite->stop = true;
}
if(this->last_x == this->body->x and this->last_y == this->body->y)
this->walking = false;
}
void update(){
this->control();
}
};
class Zombie : public Human{
private:
//Player *to_kill;
int to_kill_x, to_kill_y;
unsigned int walk_ticks_counter = 0, follow_ticks_counter = 0;
public:
Player *to_kill;
void fix_to_kill_position(){
if(this->to_kill){
this->to_kill_x = this->to_kill->body->x;
this->to_kill_y = this->to_kill->body->y;
}
else{
this->to_kill_x = this->body->x;
this->to_kill_y = this->body->y;
}
}
Zombie(int x, int y, Player *to_kill) : Human(x, y, "textures/zombie/sprite.png"){
this->sprite->set_speed(23);
this->walking_speed = 1;
this->to_kill = to_kill;
this->fix_to_kill_position();
}
void control(){
if(this->body->y < to_kill_y)
this->walk_down();
else if(this->body->y > to_kill_y)
this->walk_up();
if(this->body->x < to_kill_x)
this->walk_right();
else if(this->body->x > to_kill_x)
this->walk_left();
if(not this->walking){
this->sprite->set_frame(0);
this->sprite->fix_sheet_looking();
this->sprite->stop = true;
}
}
void update(){
if(this->follow_ticks_counter == 78){
this->fix_to_kill_position();
this->follow_ticks_counter = 0;
}
else{
this->follow_ticks_counter += 1;
}
if(this->walk_ticks_counter == 2){
this->control();
this->walk_ticks_counter = 0;
}
else{
this->walk_ticks_counter += 1;
}
if(this->last_x == this->body->x and this->last_y == this->body->y)
this->walking = false;
}
};
My goal is to create an SDL window plotting different waveforms and playing an indefinite sound of this wave. By pressing specific keys, the parameters of the wave, like the amplitude, frequency or waveform can be modified.
The problem is that even a simple sine wave which looks nice when plotted, sounds noisy. I don't understand why.
Code:
#include "Graph.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, char* argv[]){
Graph* g = new Graph();
int i;
std::cin >> i;
return 0;
}
int graphThreadFunc(void *pointer){
Graph* grid = (Graph*)pointer;
grid->init();
return 0;
}
// SDL calls this function whenever it wants its buffer to be filled with samples
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
uint8_t *stream = (uint8_t*)buffer;
Graph* graph = (Graph*)data;
for (int i = 0; i <= length; i++){
if (graph->voice.audioLength <= 0)
stream[i] = graph->getSpec()->silence; // 128 is silence in a uint8 stream
else
{
stream[i] = graph->voice.getSample();
graph->voice.audioPosition++;
// Fill the graphBuffer with the first 1000 bytes of the wave for plotting
if (graph->graphPointer < 999)
graph->graphBuffer[graph->graphPointer++] = stream[i];
}
}
}
Graph::Graph()
{
// spawn thread
SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}
SDL_AudioSpec* Graph::getSpec(){
return &this->spec;
}
void Graph::init()
{
// Init SDL & SDL_ttf
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_zero(desiredDeviceSpec);
desiredDeviceSpec.freq = 44100; // Sample Rate
desiredDeviceSpec.format = AUDIO_U8; // Unsigned 8-Bit Samples
desiredDeviceSpec.channels = 1; // Mono
desiredDeviceSpec.samples = 2048; // The size of the Audio Buffer (in number of samples, eg: 2048 * 1 Byte (AUDIO_U8)
desiredDeviceSpec.callback = SDLAudioCallback;
desiredDeviceSpec.userdata = this;
dev = SDL_OpenAudioDevice(NULL, 0, &desiredDeviceSpec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
if (dev == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
}
else {
SDL_PauseAudioDevice(dev, 1); /* pause! */
SDL_PauseAudio(1);
}
// Create an application window with the following settings:
window = SDL_CreateWindow(
WINDOW_TITLE.c_str(), // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
WINDOW_WIDTH, // width, in pixels
WINDOW_HEIGHT, // height, in pixels
SDL_WINDOW_SHOWN // flags - see below
);
// Check if the window was successfully created
if (window == NULL) {
// In case the window could not be created...
printf("Could not create window: %s\n", SDL_GetError());
return;
}
else{
voice.waveForm = Graph::Voice::WaveForm::SINE;
voice.amp = 120;
voice.frequency = 440;
SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
mainLoop();
return;
}
}
void Graph::mainLoop()
{
while (thread_exit == 0){
SDL_Event event;
bool hasChanged = false;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_KEYDOWN:
{
hasChanged = true;
if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
//pause_thread = !pause_thread;
switch (voice.waveForm){
case Voice::SINE:
{
voice.waveForm = Graph::Voice::WaveForm::TRIANGLE;
break;
}
case Voice::TRIANGLE:
{
voice.waveForm = Graph::Voice::WaveForm::RECT;
break;
}
case Voice::RECT:
{
voice.waveForm = Graph::Voice::WaveForm::SAWTOOTH;
break;
}
case Voice::SAWTOOTH:
{
voice.waveForm = Graph::Voice::WaveForm::SINE;
break;
}
default:
break;
}
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
exit();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN){
}
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){
voice.frequency -= 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){
voice.frequency += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_UP){
voice.amp += 2;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){
voice.amp -= 2;
}
else{
}
break;
}
case SDL_QUIT:
{
exit();
return;
break;
}
default: /* unhandled event */
break;
}
}
if (!pause_thread && hasChanged)
{
//SDL_PauseAudioDevice(dev, 1); // play
graphPointer = 0;
voice.audioLength = 44100;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(200);
drawGraph();
}
//voice.waveForm = Voice::WaveForm::TRIANGLE;
//SDL_Delay(n); // delay the program to prevent the voice to be overridden before it has been played to the end
//SDL_PauseAudioDevice(dev, 1); // pause
SDL_Delay(REFRESH_INTERVAL);
//SDL_PauseAudioDevice(dev, 1); // pause
}
return;
}
void Graph::drawGraph()
{
SDL_Renderer *renderer = SDL_GetRenderer(window);
if (renderer == nullptr)
renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
// Set background color
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Clear winow
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 22, 22, 22, 255);
for (int x = 0; x < WINDOW_WIDTH; x++){
uint8_t y = graphBuffer[x];
SDL_RenderDrawPoint(renderer, x, WINDOW_HEIGHT - y);
}
SDL_RenderPresent(renderer);
return;
}
void Graph::exit(){
thread_exit = 1;
// Close and destroy the window
SDL_DestroyWindow(window);
// Clean up
SDL_Quit();
}
uint8_t Graph::Voice::getSample(){
switch (waveForm){
case SINE:
{
float sineStep = 2 * M_PI * audioPosition * frequency / 44100;
return (amp * sin(sineStep)) + 128;
break;
}
case RECT:
break;
case SAWTOOTH:
break;
case TRIANGLE:
break;
default:
return 0;
}
}
And the header file:
#ifndef GRAPH_H
#define GRAPH_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
/* Constants */
const int REFRESH_INTERVAL = 50; // mseconds
const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 255;
const std::string WINDOW_TITLE = "Wave Graph";
class Graph
{
private:
SDL_Window *window; // Declare a pointer
// SDL audio stuff
SDL_AudioSpec desiredDeviceSpec;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
int thread_exit = 0;
bool pause_thread = false;
public:
Graph();
void init();
void mainLoop();
void drawGraph();
void exit();
SDL_AudioSpec* getSpec();
struct Voice{
int frequency; // the frequency of the voice
int amp; // the amplitude of the voice
int audioLength; // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
int audioPosition = 0; // counter
enum WaveForm{
SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3
} waveForm;
uint8_t getSample();
} voice;
int graphPointer = 0;
uint8_t graphBuffer[1000];
};
#endif
Your SDLAudioCallback() is writing an extra byte off the end of buffer:
void SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
...
for (int i = 0; i <= length; i++)
// ^^ huh?
{
...
}
}
Changing the <= to just < fixes the crackles on my system.
Generally C-style "byte pointer + length" APIs expect a left-closed, right-open interval: [0, length). I.e., you can access buffer[length - 1] but not buffer[length].