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].
Related
I'm trying to make a simple Snake game. The self collision and a lot of things aren't ready. The problem is: Even with no errors, running just fine, changing "close()" to "endGame()" and reverting, rebuilding the entire program, i could not render anything with SDL_RenderCopy. You will see many unnecessary things in my code and some Brazilian-Portuguese comments, prepare yourself.
The image is a 16x16 png spritesheet, using the color #ff00ff as ColorKey. There are only 4 sprites in this spritesheet, respectively: Apple, Snake's Body, Snake's Head and Snake's Tail (still unused).
Whole code:
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
using namespace std;
const int CELL_SIZE = 16;
const int CELL_WIDTH = 16;
const int CELL_HEIGHT = 16;
const int SCREEN_WIDTH = CELL_SIZE * (CELL_WIDTH-1);
const int SCREEN_HEIGHT = CELL_SIZE * (CELL_HEIGHT-1);
SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;
void loadTexture(string path);
SDL_Texture* spriteSheet = NULL;
void loadMedia();
void init();
class Snake {
int initialLength = 3; //from body (head incluse)
int length = initialLength;
int facing = DIR_UP;
//Head position in CELLS (multiplied for CELL_SIZE when drawed)
int x = 5;
int y = 5;
//UNUSED TEMPORARY COLORS - IGNORE
Uint8 color[3] = {0x22,0x88,0x44}; //Snake Color, em RGB
Uint8 headColor[3] = {0x11,0x44,0x44}; //Color for the Head of the Snek
int stepDelay = 60; //time in frames that the Snakes waits till move
int stepFramesRemaining = stepDelay;
int stepDelayReduction = 1; //reduces stepDelay every time the Snakes eats the apple
const int minStepDelay = 15; //the minimum delay
//For clipping the image (its an 16x16 png image)
const SDL_Rect clipHead = {0,8,8,8};
const SDL_Rect clipBody = {8,0,8,8};
const SDL_Rect clipTail = {8,8,8,8};
public:
enum direction { //direções nas quais a cobra pode se mover
DIR_UP,
DIR_RIGHT,
DIR_DOWN,
DIR_LEFT
};
/* The following var stores the entire Snake's body in an 2D Array. The number stored means how much "steps" it will survive.
When the value is zero, it means that there is no body in the place. The bodypart with value equivalent to "length" is the Head.
*/
int body[CELL_WIDTH][CELL_HEIGHT];
void init(); //initializes some vars
void tick(); //tick function
void stepFoward(); //moves Snake foward
void faceTo(int dir); //alters which direction Snake is faced
void eat(); //eats the apple
void draw(); //renders (or at least tries) to render the snake
int getLength(){
return length;
};
int getFacing(){
return facing;
};
} Snake;
class Food {
Uint8 color[3] = {0x85,0x22,0x10}; //RGB color of the Apple
bool visible = true;
public:
int x = 2;
int y = 2;
void respawn();
void destroy();
void draw();
} Food;
void Food::respawn(){
//teleports the apple to a random spot
x = rand() % (CELL_WIDTH-2);
y = rand() % (CELL_HEIGHT-2);
visible = true;
}
void Food::destroy(){
//Reset values
x = 0;
y = 0;
visible = false;
//resets
respawn();
}
void Food::draw(){
if(visible){
SDL_Rect rect = {x*CELL_SIZE,y*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderFillRect(gRenderer, &rect);
}
}
void Snake::init(){
//Spawns in a vertical line
for(int i=0; i<length; i++){
body[x][y+i] = length-i;
}
}
void Snake::tick(){
if(stepFramesRemaining > 0){
stepFramesRemaining--;
} else {
//when 0, moves the snake
stepFramesRemaining = stepDelay;
stepFoward();
}
}
void Snake::eat(){
//increases the body size by 1
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] > 0){
body[j][i]++;
}
}
}
length++;
if(stepDelay > minStepDelay){
stepDelay -= stepDelayReduction;
}
Food.destroy();
}
void Snake::draw(){
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_Rect rect = {0,0,0,0}; //for later use
//Draws the body and head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] == length){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,0x33,0xff,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
SDL_RenderCopy(gRenderer, spriteSheet, &clipHead, &rect);
} else if (body[j][i] > 0){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderCopyEx(gRenderer, spriteSheet, &clipBody, &rect, 0, NULL, SDL_FLIP_NONE);
SDL_SetRenderDrawColor(gRenderer,0x66,0xee,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
}
SDL_RenderFillRect(gRenderer,&rect);
}
}
//SDL_RenderFillRect(gRenderer,&rect);
}
void Snake::stepFoward(){
int headMoved = 0; //informs if the head already moved
//decreases the "body" lifespan and moves head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] > 0){
//Verifica se é a cabeça, para movê-la logo em seguida
if(body[j][i] == length && headMoved < 2){
//moves head position, looping if needed
switch(facing){
case DIR_UP:
if(y == 0){
body[x][CELL_HEIGHT-1] = length;
y = CELL_HEIGHT-1;
} else {
body[x][y-1] = length;
y--;
}
break;
case DIR_DOWN:
if(y == CELL_HEIGHT-2){
body[x][0] = length+1; //(+1 to avoid being subtracted twice)
y = 0;
} else {
body[x][y+1] = length+1;
y++;
}
break;
case DIR_LEFT:
if(x == 0){
body[CELL_WIDTH-1][y] = length;
x = CELL_WIDTH-1;
} else {
body[x-1][y] = length;
x--;
}
break;
case DIR_RIGHT:
if(x == CELL_WIDTH-2){
body[0][y] = length+1; //avoiding again the "-2" subtraction.
x = 0;
} else {
body[x+1][y] = length+1;
x++;
}
break;
}
headMoved++;
}
body[j][i]--; //decreases the "body" lifespan
}
}
}
//verifies if can eat (head in the same position as the apple)
if(x == Food.x && y == Food.y){
eat();
}
}
void Snake::faceTo(int dir){
facing = dir;
}
void init();
void close();
void init(){ //Initializes the game
gWindow = SDL_CreateWindow("· Snake ·", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
if( gRenderer == NULL ){
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
}
int imgFlags = IMG_INIT_PNG;
if(!(IMG_Init(imgFlags) & imgFlags)){
cout << "IMG INIT error!" << endl;
}
loadMedia();
Snake.init();
}
void close(){ //Closes the program
SDL_DestroyTexture(spriteSheet);
spriteSheet = NULL;
SDL_DestroyRenderer(gRenderer);
gRenderer = NULL;
IMG_Quit();
SDL_Quit();
}
void loadTexture(string path){ //Almost the same function from LazyFoo tutorial
//The final texture
SDL_Texture* newTexture = NULL;
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
if( loadedSurface == NULL )
{
printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
}
else
{
//Color key image
SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0xff, 0x00, 0xff));
//Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface);
if( newTexture == NULL ){
printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
}
//Get rid of old loaded surface
SDL_FreeSurface( loadedSurface );
}
spriteSheet = newTexture;
}
void loadMedia(){ //loads everything (it will load sound too, when i make the sounds of course)
loadTexture("spritesheet.png");
if(spriteSheet == NULL){
cout << "ERRO" << endl;
}
}
void tick(){
Snake.tick();
}
void draw(){ //Render Function
//Background
SDL_SetRenderDrawColor(gRenderer, 0xee, 0xf2, 0xf0, 0xff);
SDL_RenderClear(gRenderer);
Snake.draw();
Food.draw();
//Aplica as alterações
SDL_RenderPresent(gRenderer);
}
int main(){
srand (time (NULL));
init();
bool quit = false;
SDL_Event e; //Event Handling
while(!quit){
while(SDL_PollEvent(&e) != 0){
if(e.type == SDL_QUIT){
quit = true;
} else if(e.type == SDL_KEYDOWN){
switch(e.key.keysym.sym){
case SDLK_UP:
Snake.faceTo(Snake.DIR_UP);
break;
case SDLK_DOWN:
Snake.faceTo(Snake.DIR_DOWN);
break;
case SDLK_LEFT:
Snake.faceTo(Snake.DIR_LEFT);
break;
case SDLK_RIGHT:
Snake.faceTo(Snake.DIR_RIGHT);
break;
}
}
}
//Tick function
tick();
//Renders everything
draw();
SDL_RenderCopy(gRenderer, spriteSheet, NULL, NULL);
//Slows down the program just a bit (its not a time based frame system... yet)
SDL_Delay(3);
}
close(); //ends
return 0;
}
The portion of code who should be working, but isn't:
//Draws the body and head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] == length){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,0x33,0xff,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
SDL_RenderCopy(gRenderer, spriteSheet, &clipHead, &rect);
} else if (body[j][i] > 0){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderCopyEx(gRenderer, spriteSheet, &clipBody, &rect, 0, NULL, SDL_FLIP_NONE);
SDL_SetRenderDrawColor(gRenderer,0x66,0xee,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
}
SDL_RenderFillRect(gRenderer,&rect);
}
}
//SDL_RenderFillRect(gRenderer,&rect);
}
I started writing a little game but something with the image is not working. They're working fine at the beginning but after some moments there become black and then they disappear.
Don't be worried about the long code the relevant things with the images happen mostly in the following methods: build_mode_draw() and play_mode_draw().
I tested the program with a blue cube instead of a image and it worked fine
Probably I don't really understand how the image gets loaded
#include <SDL2/SDL.h>
#define WindowWidth 1500
#define WindowHight 800
#define ArrayGrosseBuildMode 100
#define StartFensterBlockmenge 10 // in Plockgröße bezüglich der kleineren Achse
bool end = false;
bool programm_part_run = true;
unsigned int play_mode_speed = 0;
unsigned int counter;
unsigned int counter_2;
unsigned int counter_3;
unsigned int blocksize;
unsigned int blocks_fit_in_X;
unsigned int blocks_fit_in_Y;
unsigned int play_mode_blockamount;
//unsigned int blockamount = 0;
bool build_mode_block_pos[ArrayGrosseBuildMode][ArrayGrosseBuildMode];
unsigned int play_mode_block_pos_X[WindowWidth]; // Fächer beschreiben
unsigned int play_mode_block_pos_Y[WindowHight]; // ||
//mouse variables
unsigned short int pressed_mouse_button = 0; // 0 = no , 1 = left , mouse Button pressed
unsigned int MouseX;
unsigned int MouseY;
//keyboard variables
//set window
SDL_Window* window = NULL;
//set renderer
SDL_Renderer* renderer = NULL;
//set event
SDL_Event event;
void input()
{
SDL_PollEvent(&event);
// reset variables
pressed_mouse_button = 0; // set to no mouse button pressed
switch(event.type)
{
case SDL_QUIT:
end = true;
programm_part_run = false;
break;
case SDL_MOUSEMOTION:
MouseX = event.motion.x;
MouseY = event.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
pressed_mouse_button = 1;
break;
}
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_SPACE:
programm_part_run = false;
break;
}
}
}
void put_build_mode_grid_in_renderer()
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
for(counter = 0; counter <= blocks_fit_in_Y; counter = counter + 1)
{
SDL_RenderDrawLine(renderer,0,counter * blocksize,blocks_fit_in_X*blocksize,counter * blocksize);
}
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
SDL_RenderDrawLine(renderer,counter * blocksize,0,counter * blocksize,blocks_fit_in_Y*blocksize);
}
}
void build_mode_draw()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0);
SDL_RenderClear(renderer);
put_build_mode_grid_in_renderer();
SDL_Surface * image = SDL_LoadBMP("stealcube.bmp");
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer,image);
SDL_FreeSurface(image);
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
for(counter_2 = 0; counter_2 <= blocks_fit_in_Y; counter_2 = counter_2 + 1)
{
if(build_mode_block_pos[counter][counter_2] == true)
{
SDL_Rect dstrect = { counter * blocksize, counter_2 * blocksize, blocksize, blocksize};
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
}
}
}
SDL_RenderPresent(renderer);
}
void build_mode()
{
while(programm_part_run)
{
input();
if(pressed_mouse_button == 1)
{
build_mode_block_pos[MouseX/blocksize][MouseY/blocksize] = true;
}
build_mode_draw();
}
}
void play_mode_draw()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0);
SDL_RenderClear(renderer);
SDL_Surface * image = SDL_LoadBMP("stealcube.bmp");
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer,image);
SDL_FreeSurface(image);
for(counter = 0; counter < play_mode_blockamount; counter = counter + 1)
{
SDL_Rect dstrect = { play_mode_block_pos_X[counter], play_mode_block_pos_Y[counter], blocksize, blocksize};
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
}
SDL_RenderPresent(renderer);
}
void play_mode()
{
counter_3 = 0;
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
for(counter_2 = 0; counter_2 <= blocks_fit_in_Y; counter_2 = counter_2 + 1)
{
if(build_mode_block_pos[counter][counter_2] == true)
{
play_mode_block_pos_X[counter_3] = counter*blocksize;
play_mode_block_pos_Y[counter_3] = counter_2*blocksize;
counter_3 = counter_3 + 1;
}
}
}
play_mode_blockamount = counter_3;
while(programm_part_run)
{
for(counter = 0; counter < play_mode_speed; counter = counter + 1)
{
input();
SDL_Delay(1);
}
for(counter = 0; counter <= play_mode_blockamount; counter = counter + 1)
{
if(play_mode_block_pos_Y[counter] < blocks_fit_in_Y * blocksize - blocksize)
{
play_mode_block_pos_Y[counter] = play_mode_block_pos_Y[counter] + 1;
}
}
play_mode_draw();
}
}
int main (int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow
(
"Test Fenster :)", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WindowWidth,
WindowHight,
SDL_WINDOW_SHOWN
);
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
//setup
if(WindowWidth < WindowHight)
{
blocksize = WindowWidth/StartFensterBlockmenge;
}
else
{
blocksize = WindowHight/StartFensterBlockmenge;
}
blocks_fit_in_X = WindowWidth/blocksize;
blocks_fit_in_Y = WindowHight/blocksize;
while(!end)
{
programm_part_run = true;
build_mode();
programm_part_run = true;
play_mode();
}
}
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 generating a Sine wave and send it to the SDL Audio Buffer to generate sound. All the parameter like the amplitude and frequency can be changed with the arrow keys of the keyboard.
Now, the problem is that when I change the frequency, I hear a "scratch". I understand why this is happening: I'm getting a totally wrong value when I just continue iterating my x in f(x) when the function itself has changed. But I fail to see or understand how I can fix this problem by phase shifting.
Any tips how to start?
#include "WaveGenerator.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm> // std::min
int main(int argc, char* argv[]){
WaveGenerator* wg = new WaveGenerator();
int i;
std::cin >> i;
return 0;
}
int graphThreadFunc(void *pointer){
WaveGenerator* wg = (WaveGenerator*)pointer;
wg->init();
return 0;
}
// SDL calls this function whenever it wants its buffer to be filled with samples
// length = 2048
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
uint8_t *stream = (uint8_t*)buffer;
WaveGenerator* wg = (WaveGenerator*)data; // pointer to our WaveGenerator object where the voice data is stored
for (int i = 0; i < length; i++){
if (wg->voice.audioLength <= 0)
stream[i] = wg->getSpec()->silence; // 128 is silence in a uint8 stream
else
{
stream[i] = wg->voice.getSample(); // calculate the current sample value
}
wg->voice.audioPosition++;
}
}
WaveGenerator::WaveGenerator()
{
// spawn thread
SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}
SDL_AudioSpec* WaveGenerator::getSpec(){
return &this->spec;
}
void WaveGenerator::init()
{
// Init SDL & SDL_ttf
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_zero(desiredDeviceSpec);
desiredDeviceSpec.freq = SAMPLING_RATE; // 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{
// Initial wave parameters
voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
voice.amp = 120;
voice.frequency = 440;
SDL_PauseAudioDevice(dev, 1); // pause
voice.audioLength = SAMPLING_RATE;
voice.audioPosition = 0;
SDL_PauseAudioDevice(dev, 0); // play
SDL_Delay(SAMPLING_RATE / voice.audioLength * 1000); // 44100 / length of the audio * 1000 (to get milliseconds)
mainLoop();
}
return;
}
void WaveGenerator::mainLoop()
{
bool waveHasChanged = false;
// poll SDL events until we terminate the thread
while (thread_exit == 0){
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_KEYDOWN:
{
if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
switch (voice.waveForm){
case Voice::SINE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::TRIANGLE;
break;
}
case Voice::TRIANGLE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::RECT;
break;
}
case Voice::RECT:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::SAWTOOTH;
break;
}
case Voice::SAWTOOTH:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::NOISE;
break;
}
case Voice::NOISE:
{
voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
break;
}
default:
break;
}
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
exit();
}
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){
voice.frequency -= 10;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){
voice.frequency += 10;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_UP){
voice.amp += 2;
waveHasChanged = true;
}
else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){
voice.amp -= 2;
waveHasChanged = true;
}
else{
}
break;
}
case SDL_QUIT:
{
exit();
return;
break;
}
default: /* unhandled event */
break;
}
}
if (!pause_thread && waveHasChanged)
{
// calculate phase shifting?
}
SDL_Delay(50);
}
return;
}
void WaveGenerator::exit(){
thread_exit = 1;
// Clean up
SDL_Quit();
}
WaveGenerator::Voice::Voice(){
}
uint8_t WaveGenerator::Voice::getSample(){
switch (waveForm){
case SINE:
{
return (amp * sin(2 * M_PI * audioPosition * frequency / SAMPLING_RATE)) + 128;
break;
}
// .....
default:
return 0;
}
}
and the header file:
#ifndef WAVEGENERATOR_H
#define WAVEGENERATOR_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
#include <io.h> // unistd.h for mac/linux, io.h for windows
#include <vector>
#include <fstream>
/* Window Constants */
const std::string WINDOW_TITLE = "Wave Graph";
const int WINDOW_WIDTH = 1980;
const int WINDOW_HEIGHT = 255;
/* Audio Constants */
const int SAMPLING_RATE = 44100; // number of samples per second
class WaveGenerator
{
private:
SDL_Window *window;
// SDL Audio
SDL_AudioSpec desiredDeviceSpec;
SDL_AudioSpec spec;
SDL_AudioDeviceID dev;
int thread_exit = 0;
bool pause_thread = false;
public:
WaveGenerator();
void init();
void mainLoop();
void exit();
SDL_AudioSpec* getSpec();
// SDL audio members
struct Voice{
Voice();
// WaveForm parameters
enum WaveForm{
SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3, NOISE = 4
} waveForm;
int frequency; // the frequency of the voice
int amp; // the amplitude of the voice
// SDL buffer handling members
int audioLength; // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
int audioPosition = 0; // counter
uint8_t getSample();
} voice;
};
#endif
The easiest way to change frequencies without a jump in phase by removing audioPosition from the equation:
class WaveGenerator
{
private:
double m_sinePhase;
double m_sinePhaseInc;
uint8_t WaveGenerator::Voice::getSample(){
switch (waveForm){
case SINE:
{
uint8_t sample = (amp * sin(2 * M_PI * m_sinePhase)) + 128;
m_sinePhase += m_sinePhaseInc;
return sample;
}
}
And then when you change the frequency just recompute the phase increment
m_sinePhaseInc = freq/sampleRate;
I am attempting to make a simple scrolling shooter game with SDL2. I have a moving player on a screen, and I am trying to make the player shoot a bullet using an array (so they can shoot multiple bullets) however, when I press the space bar, nothing happens, and instead the bullet image sort of flashes in the top left corner.
Heres the same code in codepad: http://codepad.org/rOhE1AqY
#include <SDL.h>
#include <stdio.h> //use for things like printf, same as cout
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
//screend dimensions& sprtie dimensions
const int SCREEN_HEIGHT = 600;
const int SCREEN_WIDTH = 400;
const int SPRITE_WIDTH = 60;
const int SPRITE_HEIGHT = 80;
const int MAX_BULLETS = 50;
SDL_Window* Window = NULL;//the window rendering to
SDL_Surface* ScreenSurface = NULL;//surface contained by window
SDL_Surface* Background = NULL;
SDL_Surface* Player = NULL;
SDL_Surface* Enemy = NULL;
SDL_Surface* Bullet = NULL;
SDL_Surface* newBullet = NULL;
SDL_Rect posPlayer, posEnemy, posBullet, posnewBullet;
const Uint8* keystate = SDL_GetKeyboardState(NULL);
SDL_Event event;
class thePlayer
{
public:
thePlayer();
void player_movement();
void show_player();
private:
};
class theBullet
{
public:
theBullet();
bool isActive;
int x_position;
int y_position;
void bullet_movement();
void add_new_bullet();
void show_bullet();
private:
};
theBullet arrayofBullets[MAX_BULLETS];
class theEnemy
{
public:
theEnemy();
void enemy_movement();
void show_enemy();
private:
};
thePlayer::thePlayer()
{
posPlayer.x = 170;
posPlayer.y = SCREEN_HEIGHT;
posPlayer.w = 20;
posPlayer.h = 30;
}
void thePlayer::player_movement()
{
if(keystate[SDL_SCANCODE_LEFT])
{
posPlayer.x -= 2;
}
if(keystate[SDL_SCANCODE_RIGHT])
{
posPlayer.x += 2;
}
if(keystate[SDL_SCANCODE_UP])
{
posPlayer.y -= 2;
}
if(keystate[SDL_SCANCODE_DOWN])
{
posPlayer.y += 2;
}
if ((posPlayer.x + SPRITE_WIDTH) > SCREEN_WIDTH)
{
posPlayer.x = (SCREEN_WIDTH - SPRITE_WIDTH);
}
if ((posPlayer.y + SPRITE_HEIGHT) > SCREEN_HEIGHT)
{
posPlayer.y = (SCREEN_HEIGHT - SPRITE_HEIGHT);
}
}
void thePlayer::show_player()
{
SDL_BlitSurface(Player, NULL, ScreenSurface, &posPlayer);
SDL_SetColorKey(Player, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
theBullet::theBullet()
{
/*posBullet.x;
posBullet.y;
posBullet.w = 10;
posBullet.h = 15;*/
}
void theBullet::bullet_movement()
{
/*if(keystate[SDL_SCANCODE_SPACE])
{
posBullet.x = posPlayer.x + 25;
posBullet.y = posPlayer.y + 10;
}
posBullet.y -= 2;
if(posBullet.y < 0)
{
posBullet.y = -50;
}*/
}
void theBullet::show_bullet()
{
//SDL_BlitSurface(Bullet, NULL, ScreenSurface, &posBullet);
//SDL_SetColorKey(Bullet, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));//removes white background
}
theEnemy::theEnemy()
{
srand (time(NULL));
posEnemy.x = rand() % 300 + 50;
posEnemy.y =0;
posEnemy.w = 35;
posEnemy.h = 60;
}
void theEnemy::enemy_movement()
{
posEnemy.y += 1;
if(posEnemy.y > SCREEN_HEIGHT)
{
posEnemy.y = SCREEN_HEIGHT +50;
}
}
void theEnemy::show_enemy()
{
SDL_BlitSurface(Enemy, NULL, ScreenSurface, &posEnemy);
SDL_SetColorKey(Enemy, SDL_TRUE, SDL_MapRGB(Player->format, 255, 255, 255));
}
bool initialise()
{
bool success = true;
if (SDL_Init(SDL_INIT_EVERYTHING) !=0)
{
cout<<"SDL_Init Error."<<SDL_GetError()<<endl;
success = false;
}
else
{
//create the window for game
Window = SDL_CreateWindow("Scrolling Shooter Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (Window == NULL)
{
cout<<"Window Error"<<SDL_GetError()<<endl;
success = false;
}
else
{
//get window surface
ScreenSurface = SDL_GetWindowSurface(Window);
}
}
return success;
}
bool LoadingMedia()
{
bool success = true;
Background = SDL_LoadBMP("background.bmp");
if (Background == NULL)
{
cout<<"Error in loading background."<<SDL_GetError()<<endl;
success = false;
}
Player = SDL_LoadBMP("spaceship.bmp");
if (Player == NULL)
{
cout<<"Error in loading player."<<SDL_GetError()<<endl;
success = false;
}
Enemy = SDL_LoadBMP("enemy.bmp");
if (Enemy == NULL)
{
cout<<"Error in loading enemy."<<SDL_GetError()<<endl;
success = false;
}
Bullet = SDL_LoadBMP("bullet.bmp");
if (Bullet == NULL)
{
cout<<"Error in loading bullet."<<SDL_GetError()<<endl;
success = false;
}
return success;
}
void closedown()
{
SDL_FreeSurface(Background);
Background = NULL;
SDL_FreeSurface(Player);
Player = NULL;
SDL_FreeSurface(Enemy);
Enemy = NULL;
SDL_DestroyWindow(Window);
Window = NULL;
SDL_Quit();
}
int main(int argc, char** argv)
{
bool quit = false;
thePlayer myPlayer;
theEnemy myEnemy;
theBullet myBullet;
if (!initialise())
{
cout<<"Failed to initialise"<<SDL_GetError()<<endl;
}
else
{
if (!LoadingMedia())
{
cout<<"Error loading media"<<SDL_GetError()<<endl;
}
}
//makes all bullets false
for (int i=0; i<MAX_BULLETS; i++)
{
arrayofBullets[i].isActive = false;
}
//GAME LOOP
while (quit == false)
{
SDL_BlitSurface(Background, NULL, ScreenSurface, NULL);
myPlayer.show_player();
myPlayer.player_movement();
while (SDL_PollEvent(&event))
{
if( event.type == SDL_QUIT )
{
quit = true;
break;
}
if(keystate[SDL_SCANCODE_SPACE])
{
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == false)
{
arrayofBullets[i].x_position = posPlayer.x + 25;
arrayofBullets[i].y_position = posPlayer.y + 10;
arrayofBullets[i].isActive = true;
break;
}
}
}
//update game objects
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
arrayofBullets[i].y_position -= 2;
if (arrayofBullets[i].y_position < 0)
{
arrayofBullets[i].isActive = false;
}
}
}
for (int i=0; i<MAX_BULLETS; i++)
{
if (arrayofBullets[i].isActive == true)
{
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
}
}
}
//myPlayer.show_player();
//myBullet.show_bullet();
//myEnemy.show_enemy();
//myPlayer.player_movement();
//myBullet.bullet_movement();
//myEnemy.enemy_movement();
SDL_UpdateWindowSurface(Window); //updates screen
}
closedown();
return 0;
}
SDL_BlitSurface(Bullet, NULL, ScreenSurface, NULL);
You haven't specified destination rect, so it will blit on left-top corner.
It should be
SDL_Rect dstrect;
dstrect.x = arrayofBullets[i].x_position;
dstrect.y = arrayofBullets[i].y_position;
dstrect.w = Bullet->w;
dstrect.h = Bullet->h;
SDL_BlitSurface(Bullet, NULL, ScreenSurface, &dstrect);