I have added these functions to my code, and my program started crashing randomly showing
Unhandled exception at 0x6C7B52E7 (SDL2.dll) in SDL_2.exe: 0xC0000005: Access violation writing location 0xFF00000C. Debugger shows at completely random pieces of code, that worked perfectly before, which leads me to think, that these new functions mess up the memory.
void ObstacleSet::move()
{
Obstacles.push_back(Obstacle(mScreenWidth, mScreenHeight / 6 + 100, 10, 10, Coin, -29, 0));
Obstacles.push_back(Obstacle(mScreenWidth, mScreenHeight / 6 + 120, 10, 10, Coin, -29, 0));
Obstacles.push_back(Obstacle(mScreenWidth, mScreenHeight / 6 + 140, 10, 10, Coin, -29, 0));
Obstacles.push_back(Obstacle(mScreenWidth, mScreenHeight / 6 + 160, 10, 10, Coin, -29, 0));
Obstacles.push_back(Obstacle(mScreenWidth, mScreenHeight / 6 + 180, 10, 10, Coin, -29, 0));
for (auto &i : Obstacles)
{
i.move();
}
}
void ObstacleSet::outCheck(unsigned int &score)
{
for (unsigned int i = 0; i < getSize();)
{
if (getVelX(i) < 0 && (getX(i) + getW(i) <= 0))
{
eraseObstacle(i);
score++;
}
else if (getVelX(i) > 0 && (getX(i) >= mScreenWidth))
{
eraseObstacle(i);
score++;
}
else if (getVelY(i) > 0 && (getY(i) + getH(i) >= mScreenHeight))
{
eraseObstacle(i);
score++;
}
else if (getVelY(i) < 0 && (getY(i) <= 0))
{
eraseObstacle(i);
score++;
}
else
{
++i;
}
}
}
Apparently, i have some memory leak, or a dangling pointer somehow appears, but i just can not see where. Maybe i have a wrong constructor or something.
ObstacleSet is a class, that holds all Obstacles in a list.
#ifndef MOBSTSET_H
#define MOBSTSET_H
#include <SDL.h>
#include <cstdlib>
#include "my_OBSTACLES.h"
#include <list>
class ObstacleSet
{
public:
ObstacleSet(int ScreenWidth, int ScreenHeight);
int mScreenWidth;
int mScreenHeight;
void clear();
//Function, that moves the set depending on current time
void move();
//Render set on screen
void render(SDL_Renderer *Renderer);
unsigned int getSize();
float getX(int);
float getY(int);
int getW(int);
int getH(int);
float getVelX(int);
float getVelY(int);
Type GetType(int);
void eraseObstacle(int);
void outCheck(unsigned int &score);
std::list<Obstacle>::iterator getEnd();
private:
//Vector to hold all obstacles
std::list <Obstacle> Obstacles;
};
#endif
These appear in my main game loop one after another
Obstacle class header
#ifndef MYOBSTACLE_H
#define MYOBSTACLE_H
#include <SDL.h>
#include <cmath>
#include <vector>
#include <cstdlib>
enum Type
{
Wall,
Coin
};
class Obstacle
{
public:
Obstacle();
//Initializer with parameters
Obstacle(float x, float y, int w, int h, Type type, float VelocityX = 0, float VelocityY = 0);
float VelocityX = 0;
float VelocityY = 0;
float xPos;
float yPos;
int mWidth;
int mHeight;
//Type of obstacle
Type OBSType;
//Render obstacle on screen
void render(SDL_Renderer *Renderer);
//Move obstacle
void move();
int mRed = 200;
int mGreen = 200;
int mBlue = 20;
bool color_up = false;
};
#endif
These are my constructors for Obstacle
Obstacle::Obstacle() : xPos(10), yPos(10), mWidth(10), mHeight(10), VelocityX(0), VelocityY(0), OBSType(Coin), mRed(200), mGreen(200), mBlue(20), color_up(false)
{
printf("Obstacle created!");
}
Obstacle::Obstacle(float x, float y, int w, int h, Type type, float velocityX, float velocityY) : xPos(x), yPos(y), mWidth(w), mHeight(h), VelocityX(velocityX), VelocityY(velocityY), OBSType(type), mRed(200), mGreen(200), mBlue(20), color_up(false)
{
}
Here are some functions of ObstacleSet
void ObstacleSet::eraseObstacle(int number)
{
unsigned N = number;
std::list<Obstacle>::iterator i = Obstacles.begin();
if (Obstacles.size() > N)
{
std::advance(i, N);
}
Obstacles.erase(i);
}
unsigned int ObstacleSet::getSize()
{
return Obstacles.size();
}
float ObstacleSet::getX(int number)
{
unsigned N = number;
std::list<Obstacle>::iterator i = Obstacles.begin();
if (Obstacles.size() > N)
{
std::advance(i, N);
}
return i->xPos;
}
std::list<Obstacle>::iterator ObstacleSet::getEnd()
{
return Obstacles.end();
}
SDL Initialization code
bool init()
{
//Initialization flag
bool success = true;
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0)
{
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
//Set texture filtering to linear
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
{
printf("Warning: Linear texture filtering not enabled!");
}
//Initialize SDL_mixer
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
{
printf("SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError());
success = false;
}
//Check for joysticks
if (SDL_NumJoysticks() < 1)
{
printf("Warning: No joysticks connected!\n");
}
else
{
//Load joystick
gGameController = SDL_JoystickOpen(0);
if (gGameController == NULL)
{
printf("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError());
}
}
//Create window
gWindow = SDL_CreateWindow("Jello demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (gWindow == NULL)
{
printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Create vsynced renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (gRenderer == NULL)
{
printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
//Initialize renderer color
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
success = false;
}
//Initialize SDL_ttf
if (TTF_Init() == -1)
{
printf("SSDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError());
success = false;
}
}
return success;
}
Maybe I have got a memory leak or a dangling pointer, but I can't find where the mistake is. I will be VERY glad if anyone could find where i am wrong. Write me if you need any additional code pieces
Unless you have an invariant somewhere that guarantees that ObstacleSet never calls anything on an empty Obstacles list there is a potential problem.
This code has undefined behaviour on an empty list
float ObstacleSet::getX(int number)
{
unsigned N = number;
std::list<Obstacle>::iterator i = Obstacles.begin();
if (Obstacles.size() > N)
{
std::advance(i, N);
}
return i->xPos;
}
Trying to fix it
float ObstacleSet::getX(int number)
{
unsigned N = number;
std::list<Obstacle>::iterator i = Obstacles.begin();
if (Obstacles.size() > N)
{
std::advance(i, N);
}
if (i != Obstacles.end())
return i->xPos;
// handle empty
return ERRORPOS; // or throw or assert or ...
}
Same with all other functions that access the list.
Some issues I see in the code:
In ObstacleSet::move() you add five obstacles to the list. It looks like the list can grow indefinitely, resulting in memory overflow.
You don't check if Obstacles are empty in eraseObstacle(), getX() and perhaps in other functions which you haven't shown.
So apparently, there error was not in the code i provided. I initialized SDL_mixer wrongly. I loaded audio like this
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0), but sampling rate of wav file i provided was not MIX_DEFAULT_FORMAT, which is 22050.
Anyway, thanks to everybody, who tried to help me with my code. It was a pleasant experience to use stackoverflow for the fisrt time.
Related
I recently refactored my SDL2 C++ code using structs. It compiles and runs fine (using Visual Studio 2019), but is now throwing out an inconsistent warning:
C26496 Variable 'Graphic::X' is uninitialized. Always initialize a member variable.
This repeats for X = h, w, x, and y for the lines of code representing the renderTexture function. However, the warning does not show up for the mouseOverRect function that comes prior, both of which use Graphic struct variables in the exact same way.
I have included the relevant parts of my code below. I'm trying to figure out what caused the warning and why it didn't repeat for both functions?
header file
int mouse_x;
int mouse_y;
struct Graphic
{
std::string name;
std::string type;
int x;
int y;
int h;
int w;
};
source file
//Initialization
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Rect* clip = NULL;
//Log Error Function
void logSDLError(std::ostream &os, const std::string &msg)
{
os << msg << " error: " << SDL_GetError() << std::endl;
}
//Mouse Over Rectangle Function
bool mouseOverRect(int x, int y, Graphic &graphic)
{
if ((x <= graphic.x + graphic.w) && (x > graphic.x) &&
(y <= graphic.y + graphic.h) && (y > graphic.y))
return true;
else
return false;
//Render Texture at Destination Function
void renderTexture(SDL_Texture* texture, SDL_Renderer* renderer, Graphic &graphic,
SDL_Rect *clip = nullptr)
{
SDL_Rect dest;
dest.x = graphic.x;
dest.y = graphic.y;
dest.h = graphic.h;
dest.w = graphic.w;
SDL_RenderCopy(renderer, texture, clip, &dest);
}
// Main Function
int main(int, char**)
{
//Start up SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
logSDLError(std::cout, "Cannot initialize SDL video.");
}
//Setup window and renderer
SDL_Window *window = SDL_CreateWindow("Dragon Hunt", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, Screen_Width, Screen_Height, SDL_WINDOW_RESIZABLE);
if (window == nullptr)
{
logSDLError(std::cout, "Cannot create window.");
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == nullptr)
{
logSDLError(std::cout, "Cannot create renderer.");
}
struct Graphic createbutton;
createbutton.name = "Create Forest";
createbutton.type = "longbutton";
createbutton.x = 0;
createbutton.y = 1 * (Screen_Width / 5) + 5;
createbutton.h = Screen_Width / 10;
createbutton.w = Screen_Width / 5;
SDL_Event e;
//For tracking if we want to quit
bool quit = false;
while (!quit) {
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEMOTION:
mouse_x = e.motion.x;
mouse_y = e.motion.y;
if (mouseOverRect(mouse_x, mouse_y, createbutton))
useCreate_Clip = CREATE_HOVER;
else
useCreate_Clip = CREATE_DEFAULT;
break;
}
renderTexture(longbutton_image, renderer, createbutton, &create_clips[useCreate_Clip]);
}
None of your structure's members are initialized. They are assigned to after initialization would have been done.
Initialize your structure when you declare it, something like
Graphic createbutton {
"Create Forest",
"longbutton",
0,
1 * (Screen_Width / 5) + 5,
Screen_Width / 10,
Screen_Width / 5
};
Note that you don't need the struct keyword here.
Question:
First off, I'm very new to C++ so I'm just starting out. One of the exercises I'm currently on is having me create a "Particle Explosion" as you can see in the window title then have it flash different colors as you can see in the main.cpp.
My problem:
Whenever I run it in VS2015 I get my console to open, the window opens, but it doesn't flash any colors, it just stays white. Everything seems to be working just fine and it doesn't freeze or have any errors. Just. No. Colors.
Does anyone see an error I'm making? Once I created it I compared it to the exercise, but everything seems to be 100% exactly the same.
I'm on W7, VS2015 and using SDL2 lib.
main.cpp
#include <iostream>
#include <SDL.h>
#include "Screen.h"
#include <math.h>
#undef main
using namespace std;
using namespace caveofprogramming;
int main() {
Screen screen;
if (screen.init() == false) {
cout << "Error initialzing SDL." << endl;
}
while (true) {
// Update particles
// Draw Particles
int elapsed = SDL_GetTicks();
unsigned char green = (1 + sin(elapsed * 0.001)) * 128;
unsigned char red = (1 + sin(elapsed * 0.002)) * 128;
unsigned char blue = (1 + sin(elapsed * 0.003)) * 128;
for (int y = 0; y < Screen::SCREEN_HEIGHT; y++) {
for (int x = 0; x < Screen::SCREEN_WIDTH; x++) {
screen.setPixel(x, y, red, green, blue);
}
}
//Draw the screen
// Check for messages/events
if (screen.processEvents() == false) {
break;
}
}
screen.close();
return 0; // usually when your program runs ok it returns 0
}
Screen.cpp
#include "Screen.h"
#include <iostream>
namespace caveofprogramming {
Screen::Screen() :
m_window(NULL), m_renderer(NULL), m_texture(NULL), m_buffer(NULL) {
}
bool Screen::init() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
return false;
}
m_window = SDL_CreateWindow("Particle Fire Explosion",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (m_window == NULL) {
SDL_Quit();
return false;
}
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC);
m_texture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
if (m_renderer == NULL) {
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
if (m_texture == NULL) {
SDL_DestroyRenderer(m_renderer);
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
m_buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
memset(m_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
return true;
}
void Screen::clear() {
memset(m_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
}
void Screen::setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue) {
if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT) {
return;
}
Uint32 color = 0;
color += red;
color <<= 8;
color += green;
color <<= 8;
color += blue;
color <<= 8;
color += 0xFF;
m_buffer[(y * SCREEN_WIDTH) + x] = color;
}
void Screen::update() {
SDL_UpdateTexture(m_texture, NULL, m_buffer, SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(m_renderer);
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
SDL_RenderPresent(m_renderer);
}
bool Screen::processEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
return false;
}
}
return true;
}
void Screen::close() {
delete[] m_buffer;
SDL_DestroyRenderer(m_renderer);
SDL_DestroyTexture(m_texture);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
}
Screen.h
#pragma once
#include <SDL.h>
namespace caveofprogramming {
class Screen {
public:
const static int SCREEN_WIDTH = 800;
const static int SCREEN_HEIGHT = 600;
private:
SDL_Window *m_window;
SDL_Renderer *m_renderer;
SDL_Texture *m_texture;
Uint32 *m_buffer;
public:
Screen();
bool init();
bool processEvents();
void update();
void clear();
void setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue);
void close();
};
}
As Elderbug said, I just had to add the function to my object to render.
//Draw the screen
screen.update();
thanks!
I'am new there. I've been learning classes and tried to make a very simple platform game. But I have problem now. I've wanted to set Class "Player" to collide with 2 objects of Class "Block" But Collision do not work for one of them.
Here is my code:
#include <iostream>
#include <SDL.h>
#include <SDL_image.h>
#undef main
class Block
{
private:
SDL_Texture *BlockTexture;
public:
Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height);
~Block();
void Draw(SDL_Renderer *renderTarget);
SDL_Rect BlockPos;
};
Block::Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height)
{
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
BlockTexture = SDL_CreateTextureFromSurface(renderTarget, surface);
}
SDL_FreeSurface(surface);
BlockPos.x = xPos;
BlockPos.y = yPos;
BlockPos.w = Width;
BlockPos.h = Height;
}
Block::~Block()
{
SDL_DestroyTexture(BlockTexture);
}
void Block::Draw(SDL_Renderer *renderTarget)
{
SDL_RenderCopy(renderTarget, BlockTexture, NULL, &BlockPos);
}
class Player
{
private:
SDL_Texture *Texture;
float moveSpeed;
float jumpSpeed;
int falling = 0;
SDL_Scancode keys [3];
public:
SDL_Rect PlayerPos;
Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int PosY, int Width, int Height);
~Player();
void Update(float delta, const Uint8 *Keystate);
void Draw(SDL_Renderer *renderTarget);
bool Collision(Block &p);
};
Player::Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int PosY, int Width, int Height)
{
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
Texture = SDL_CreateTextureFromSurface(renderTarget, surface);
}
SDL_FreeSurface(surface);
PlayerPos.x = PosX;
PlayerPos.y = PosY;
PlayerPos.w = Width;
PlayerPos.h = Height;
keys[0] = SDL_SCANCODE_UP;
keys[1] = SDL_SCANCODE_LEFT;
keys[2] = SDL_SCANCODE_RIGHT;
moveSpeed = 200.f;
jumpSpeed = 100.f;
}
Player::~Player()
{
SDL_DestroyTexture(Texture);
}
void Player::Update(float delta, const Uint8 *KeyState)
{
if(KeyState[keys[0]])
{
PlayerPos.y -= moveSpeed * delta;
}
if(KeyState[keys[1]])
{
PlayerPos.x -= (moveSpeed / 2) * delta;
}
if(KeyState[keys[2]])
{
PlayerPos.x += moveSpeed * delta;
}
if(falling == 0)
{
PlayerPos.y += jumpSpeed * delta;
}
}
void Player::Draw(SDL_Renderer *renderTarget)
{
SDL_RenderCopy(renderTarget, Texture, NULL, &PlayerPos);
}
bool Player::Collision(Block &p)
{
if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
{
falling = 0;
return true;
}
else
falling = 1;
return false;
}
SDL_Texture *LoadTexture(std::string filePath, SDL_Renderer *Renderer)
{
SDL_Texture *texture = NULL;
SDL_Surface *surface = IMG_Load(filePath.c_str());
{
texture = SDL_CreateTextureFromSurface(Renderer, surface);
}
SDL_FreeSurface(surface);
return texture;
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window = SDL_CreateWindow("Platform", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
SDL_Renderer *renderTarget = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
int imgFlags = IMG_INIT_PNG;
int currentTime = 0;
int previousTime = 0;
float delta = 0;
const Uint8 *Keystate;
Player Player(renderTarget, "BlockP.png", 100, 100, 50, 50);
Block Block1(renderTarget, "Block.png", 0, 500, 800, 100);
Block Block2(renderTarget, "Block.png", 100, 300, 300, 50);
bool isRunning = true;
SDL_Event ev;
while(isRunning)
{
Keystate = SDL_GetKeyboardState(NULL);
Player.Collision(Block1);
Player.Collision(Block2);
previousTime = currentTime;
currentTime = SDL_GetTicks();
delta = (currentTime - previousTime) / 1000.0f;
Player.Update(delta, Keystate);
while(SDL_PollEvent(&ev) != 0)
{
if(ev.type == SDL_QUIT)
isRunning = false;
}
SDL_RenderClear(renderTarget);
Player.Draw(renderTarget);
Block1.Draw(renderTarget);
Block2.Draw(renderTarget);
SDL_RenderPresent(renderTarget);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderTarget);
window = NULL;
renderTarget = NULL;
SDL_Quit();
return 0;
}
The problem with your code is that each call to Player.Collision overwrites the "falling" variable.
Player.Collision(Block1); //this call calculates a falling value
Player.Collision(Block2); //...then this call overwrites falling with a new value
So effectively your code is only testing if the player is colliding with Block2, so collisions with Block1 are ignored.
Currently your Collision function is:
bool Player::Collision(Block &p)
{
if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
{
falling = 0;
return true;
}
else
falling = 1;
return false;
}
Firstly, your "return false;" is not actually part of the else, as you don't have {}. In this particular case it makes no difference as the else is exited and then the return happens but your indentation would suggest you expect the "return false;" line to be executed as part of the else block so you should put:
else
{
falling = 1;
return false;
}
Next you want to say if you have already detected a collision (eg, with Block1) then don't set falling to 1, to do this add an if statement.
else
{
if(falling != 0) //if we haven't already detected a collision this frame
{
falling = 1;
}
return false;
}
You will however need to set falling back to 1 at the start of each frame, otherwise if a collision is detected on one frame then the player will never fall on subsequent frames, even if they are not colliding with a block.
As a side note, your Player.Update code modifies the player's y position if falling == 0, this seems counter intuitive as usually 0 is false and 1 is true, hence you seem to be saying if not falling then update y, where as it should be if falling update y. Personally I would use a bool rather than an int to hold the value of falling, and then say if(falling) update y, this would make your code clearer.
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].
I have been struggling with this for a while and was wondering if anyone could help. I am trying to make a particle sample using C++ and SDL1.3 and I have had great success up until this point. The program compiles and the screen opens and nothing happens. When I run the debugger I get this error:
Unhandled exception at 0x0102414a in SDL 1.3 Space.exe: 0xC0000005: Access violation reading location 0x00000008.
The program '[7272] SDL 1.3 Space.exe: Native' has exited with code -1073741819 (0xc0000005).
and it points to this piece of the code:
bool particle::isDead()
{
return (SDL_GetTicks() >= endTime || x == 0 || y == 0 || x == SDL_GetVideoSurface()->w -1 || y == SDL_GetVideoSurface()->h - 1);
}
It would be greatly appreciated if someone would be so kind as to help me and /or point me in the right direction.
This is the entire program:
#include "SDL.h"
#include "common.h"
#include <stdio.h>
#include <string>
#include <cstdlib>
#include <vector>
#include <ctime>
/*static SDL_Texture *background = 0; //background
SDL_Renderer *renderer;
void render()
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, background, NULL, NULL); //Display background
SDL_RenderPresent(renderer);
endFrame = SDL_GetTicks();
};*/
class particle
{
float x, y, xvel, yvel;
Uint32 endTime;
Uint8 color;
public:
particle( float X, float Y, float Xvel, float Yvel, int life, Uint8 Color );
void move();
void show();
bool isDead();
};
particle::particle( float X, float Y, float Xvel, float Yvel, int life, Uint8 Color )
{
x = X;
y = Y;
xvel = Xvel;
yvel = Yvel;
endTime = SDL_GetTicks() + life;
color = Color;
}
void particle::move()
{
x += xvel;
y += yvel;
if ( x < 0 )
x = 0;
if ( y < 0 )
y = 0;
if ( x > SDL_GetVideoSurface() -> w)
x = SDL_GetVideoSurface() -> w - 1;
if ( y > SDL_GetVideoSurface() -> h)
y = SDL_GetVideoSurface() -> h -1;
}
void particle::show()
{
Uint8* pixels = (Uint8*) SDL_GetVideoSurface()->pixels;
Uint8* pixel = pixels + (int) y * SDL_GetVideoSurface()->pitch + (int) x;
*pixel = color;
}
bool particle::isDead()
{
return (SDL_GetTicks() >= endTime || x == 0 || y == 0 || x == SDL_GetVideoSurface()->w -1 || y == SDL_GetVideoSurface()->h - 1);
}
class particleEngine
{
std::vector <particle*> particles;
int x, y, maxparticle;
public:
particleEngine( int maxpart, int X, int Y );
~particleEngine();
void refresh();
};
particleEngine::particleEngine( int maxpart, int X, int Y )
{
x = X;
y = Y;
maxparticle = maxpart;
for ( int i = 0; i < maxparticle; i++ )
particles.push_back (new particle( x + rand()%6-3, y + rand()%6-3, rand()%10 + (float)rand()/(float)RAND_MAX - 5, rand()%10 + (float)rand()/(float)RAND_MAX - 5, 500 + rand()%1000, 0));
}
particleEngine::~particleEngine()
{
for ( int i = 0; i < maxparticle; i++)
delete particles[i];
}
void particleEngine::refresh()
{
for ( int i = 0; i < maxparticle; i++)
{
if ( particles[i]->isDead())
{
delete particles[i];
particles[i] = new particle( x + rand()%6-3, y + rand()%6-3, rand()%10 + (float)rand()/(float)RAND_MAX - 5, rand()%10 + (float)rand()/(float)RAND_MAX - 5, 500 + rand()%1000, 0);
}
else
{
particles[i]->move();
particles[i]->show();
}
}
}
int main( int argc, char* argv[] )
{
bool running = true;
const int FPS = 30;
Uint32 startFrame;
srand (time(0));
particleEngine ps(1000, SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
SDL_Window *window; /* main window */
SDL_Renderer *renderer;
if (SDL_Init(SDL_INIT_EVERYTHING)!= 0)
{
printf("Could not initialize SDL");
}
window = SDL_CreateWindow("SDL 1.3 Particles", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// Body of the program goes here.
while (running)
{
startFrame = SDL_GetTicks();
SDL_Event event;
//render();
//While there's events to handle
while( SDL_PollEvent( &event ) )
{
//If the user has Xed out the window
if (event.type == SDL_QUIT)
{
running = false;
}
}
//SDL_FillRect(renderer, &renderer->clip_rect, SDL_MapRGB(renderer->format, 0x00,0x00, 0x00));
ps.refresh();
//SDL_RenderCopy(renderer, 0, 0, 0);
SDL_RenderPresent(renderer);
//SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
//SDL_RenderClear(renderer);
if (1000/FPS>SDL_GetTicks()-startFrame)
SDL_Delay(1000/FPS-(SDL_GetTicks()-startFrame));
}
SDL_Quit();
return 0;
}
Access violations mean you are dereferencing null pointers(or pointers to memory you don't have access to), so this would mean (assuming the debugger synced with the source correctly) that SDL_GetVideoSurface is returning null, so you'll probably wanna throw a few checks in isDead. Secondly, its probably a good idea to store the w/h of the surface minus 1 in your class at during the creation of the surface, should mean a little less computational overhead along with shorter code.