From this Lazy Foo tutorial (https://lazyfoo.net/tutorials/SDL/21_sound_effects_and_music/index.php) I wrote the following lines of code:
#include <SDL.h>
#include <SDL_mixer.h>
bool running = true;
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_Window* window = SDL_CreateWindow("testing musique", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Event quit;
Mix_Music* music;
while (running) {
while(SDL_PollEvent(&quit)){
switch(quit.type) {
case SDL_QUIT:
running = false;
break;
}
}
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
music = Mix_LoadMUS("../pikachu/keypress_BMP/beat.wav");
Mix_PlayMusic(music, -1);
SDL_SetRenderDrawColor(renderer, 20, 20, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
Mix_FreeMusic(music);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
I even used the same audio file (beat.wav) as Lazy Foo did in his tutorial, but while his program runs without a hitch, mine plays the wav too fast (despite the fact that I checked every parameters to make sure mine matched with his). I tried decreasing the frequency parameter in Mix_OpenAudio, but while the wav did slow down, so did the pitch, and it should not have made sense to do this in the first place. What should I do?
The audio subsystem should only be opened once, before the main loop, meanwhile you open it in each iteration of the loop. The same goes for loading the file into memory — again on each iteration of the loop, instead of once. So the correct code structure should look like this:
#include <SDL.h>
#include <SDL_mixer.h>
int main(int argc, char** argv) {
bool running = true;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_Window* window = SDL_CreateWindow("testing musique", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// open audio and load music file before main loop
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
Mix_Music* music = Mix_LoadMUS("../pikachu/keypress_BMP/beat.wav");
SDL_Event event;
while (running) {
while(SDL_PollEvent(&event)){
switch(event.type) {
case SDL_KEYDOWN:
switch(event.key.keysym.sym) {
case SDLK_P:
Mix_PlayMusic(music, -1);
break;
case SDLK_S:
Mix_HaltMusic();
break;
}
case SDL_QUIT:
running = false;
break;
}
}
SDL_SetRenderDrawColor(renderer, 20, 20, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
// close audio and free the music after main loop
Mix_CloseAudio();
Mix_FreeMusic(music);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
Don't forget about Mix_CloseAudio and about error checking.
Related
In the event handling loop of SDL2, calling the method SDL_GetMouseState(&x,&y); or using event.motion.x and event.motion.y for the relative mouse coordinates makes SDL2 responsiveness VERY SLUGGISH. Whats weird is SDL_GetMouseState()is alot faster than event.motion.x and y, however they are both unbearably bad. Is there any other way of getting the mouse pos? You can even try this. I setup a simple text program in SDL2 to test something responsive such as scrolling, where you offset the y values. Try with and without vsync and with and without getting the mouse pos this way. I am currently using linux mint.
Code: (You will need arial.ttf in your folder where the project is if using codeblocks like i am)
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_image.h>
#include <string>
using std::string;
using std::to_string;
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window *window = SDL_CreateWindow("Test Program", 0, 30, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_Event event;
SDL_Point mousePos = {0,0};
///Fps vars
int fpsCounter, fpsStart, fpsEnd;
fpsStart = SDL_GetTicks();
fpsEnd = SDL_GetTicks();
fpsCounter = 0;
TTF_Font *fpsFont = TTF_OpenFont("arial.ttf", 30);
SDL_Surface *fpsSurface = TTF_RenderText_Blended(fpsFont, "FPS: ", {0,0,0});
SDL_Texture *fpsTexture = SDL_CreateTextureFromSurface(renderer, fpsSurface);
SDL_FreeSurface(fpsSurface);
int textW, textH, yVal;
yVal = 50;
SDL_QueryTexture(fpsTexture, NULL, NULL, &textW, &textH);
SDL_Rect fpsRect = {1000, yVal, textW, textH};
bool running = true;
while (running)
{
while ( SDL_PollEvent(&event) )
{
if (event.type == SDL_QUIT)
{
running = false;
break;
}
else if (event.type == SDL_MOUSEMOTION){
int x,y;
SDL_GetMouseState(&x,&y);
mousePos = {x,y};
break;
}
else if (event.type == SDL_MOUSEWHEEL){
if (event.wheel.y > 0){ ///Scrolling up here
yVal -= 50;
fpsRect.y = yVal;
break;
}
if (event.wheel.y < 0){ ///Scrolling down here
yVal += 50;
fpsRect.y = yVal;
break;
}
}
}
SDL_SetRenderDrawColor(renderer, 255,255,255,255);
SDL_RenderClear(renderer);
//Update every 0.5s (500ms)
fpsEnd = SDL_GetTicks();
fpsCounter += 2;
if ( (fpsEnd-fpsStart) > 500 ){
fpsStart = SDL_GetTicks();
SDL_DestroyTexture(fpsTexture);
///Change text
string newText = ("FPS: " + to_string(fpsCounter));
fpsSurface = TTF_RenderText_Blended(fpsFont, newText.c_str(), {0,0,0});
fpsTexture = SDL_CreateTextureFromSurface(renderer, fpsSurface);
SDL_FreeSurface(fpsSurface);
SDL_QueryTexture(fpsTexture, NULL, NULL, &textW, &textH);
fpsRect = {1000, yVal, textW, textH};
fpsCounter = 0;
}
SDL_RenderCopy(renderer, fpsTexture, NULL, &fpsRect);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(fpsTexture);
TTF_CloseFont(fpsFont);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
TTF_Quit();
SDL_Quit();
return 0;
}
I don't agree that turning off VSYNC is a solution. It shouldn't be like this with VSYNC on regardless. It isn't like this in windows for some reason, it works fine there. Capping the FPS with SDL_Delay() to the refresh rate of the display also gives me the same problem.
The problem is that you "break" every time you get an event. So, your event queue is full all the time as you only process one event per draw.
Replace "break" with "continue" and process all events.
I'm doing Lazy Foo's tutorial on SDL (I'm using SDL2-2.0.9), and at the texture rendering part I encountered the following problem: the program compiles and runs as expected, no issue here, but when I close the window, the console doesn't close and the process continues running, so I have to close the console separately.
When I tried to debug it, I found out that the program indeed leaves the main cycle and reaches the "return 0" line in the main function successfully, but then it just hangs like that until I close the console.
The issue is only present when I use the SDL renderer with any option other than SDL_RENDERER_SOFTWARE. If I use SDL_RENDERER_SOFTWARE - the program closes as expected. With other options it stays at "return 0" running other threads (crypt32.dll, ntdll.dll and nvd3dum, in this order in the thread view, meaning that the process is stuck in crypt32).
I'm aware that my main function is not the "real main" as it has been hijacked by SDL, so exit(0) works fine as an ad-hoc solution. But I want to know, why exactly does that happen and is there any other way to fix this, so that I don't have to use exit(0) ?
Here is an example (simplified) code, which demonstrates this issue for me:
#include "SDL.h"
#include <stdio.h>
int main(int argc, char *argv[]) {
SDL_Window *win = NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *bitmapTex = NULL;
SDL_Surface *bitmapSurface = NULL;
int width = 640, height = 480;
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("Could not initialize SDL");
return 1;
}
win = SDL_CreateWindow("Hello World", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
bitmapSurface = SDL_LoadBMP("res/x.bmp");
bitmapTex = SDL_CreateTextureFromSurface(renderer, bitmapSurface);
SDL_FreeSurface(bitmapSurface);
bool quit = false;
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = true;
}
}
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, bitmapTex, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(bitmapTex);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
printf("REACHED RETURN 0");
return 0;
}
Works as intended, but after closing the window I see "REACHED RETURN 0" printed in console and that's it, the console stays there. The code can be simplified further, the issue will be present as long as there is an instance of SDL_Renderer created.
UPD: The callstack during the hanging:
> ntdll.dll!_NtWaitForMultipleObjects#20()
KernelBase.dll!_WaitForMultipleObjectsEx#20()
crypt32.dll!ILS_WaitForThreadProc()
kernel32.dll!#BaseThreadInitThunk#12()
ntdll.dll!__RtlUserThreadStart()
ntdll.dll!__RtlUserThreadStart#8()
UPD2: The problem is not with the loop at all, I created the simplest application where I just create a window and a renderer and then return 0, it still gives me a hanging console. Like this:
#include <SDL.h>
int main(int argc, char* args[])
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) return 1;
SDL_Window* window = SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
return 0;
}
Same thing when I destroy them properly. The problem is in the renderer.
UPD3: Here is the Parallel Stack window during the "hanging". There is no "main" thread since I close it successfully, these are the threads which stop the program from closing properly. Other than that, it doesn't give me any understanding of the problem.
I am 100% sure I have set the SDL library to work properly
#include <iostream>
#include <SDL.h>
using namespace std;
int main(int argc, char* argv\[\]) {
// Initializing SDL
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window = 0;
// Creating the window
window = SDL_CreateWindow("Hello World", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, 640, 480,SDL_WINDOW_SHOWN);
// Just so I can see the window because it goes away immediately.
SDL_Delay(5000);
SDL_Quit();
return 0;
}
After you create the window, you need to handle events using SDL_PollEvent. Instead of SDL_Delay(5000), do something like this:
// ... setup (SDL_Init, SDL_CreateWindow, etc.)
SDL_Event event;
for(;;) {
while(SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT:
goto done;
}
}
}
done:
SDL_Quit();
return 0;
There's this extremely odd bug, which came seemingly out of nowhere, where my SDL 2 application has had a massive drop in frame rate (from ~60 to ~30). What's really odd about this bug is that while I move my cursor around quickly and outside of the window the frame rate speeds up back to normal.
What could be the issue? Or, how can I properly limit the framerate to exactly 60 (or whatever I please)?
EDIT: Here's the skeleton code which successfully replicates the problem:
#include <cstdio>
#include <SDL.h>
#define WIDTH 640
#define HEIGHT 480
#define FPS 60
using namespace std;
int main(int argc, char** args) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Cool Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
SDL_Surface* winsurf = SDL_GetWindowSurface(window);
SDL_Renderer* winren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(winren, 255, 255, 255, 255);
bool quit = false;
int counter = 0;
int looptime = SDL_GetTicks();
while (!quit) {
SDL_RenderClear(winren);
SDL_Event e;
while (SDL_PollEvent(&e))
if (e.type == SDL_QUIT)
quit = true;
printf("%d\n", counter++); // notice how quickly the counter speeds up
// while moving your cursor around outside
// of the window
if (SDL_GetTicks() - looptime < 1000/FPS)
SDL_Delay(1000/FPS - (SDL_GetTicks() - looptime));
looptime = SDL_GetTicks();
SDL_RenderPresent(winren);
}
SDL_Quit();
}
If anyone's interested, I fixed the problem by enabling Vsync. I added the SDL_RENDERER_PRESENTVSYNC flag to my call, like so:
SDL_Renderer* winren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
This makes animation much more fluid.
I've been having a lot of trouble trying to render text in SDL with TTF to an SDL window. I'm a little new to C++ and would like this explained in a bit of a 'newbie' way. (with some example code, if possible?)
Thanks!
Here is a simple example. Make sure that you have arial.ttf, or your truetype font of choice in your directory. You will need to link with -lSDL -lSDL_ttf.
#include "SDL.h"
#include "SDL_ttf.h"
SDL_Surface* screen;
SDL_Surface* fontSurface;
SDL_Color fColor;
SDL_Rect fontRect;
SDL_Event Event;
TTF_Font* font;
//Initialize the font, set to white
void fontInit(){
TTF_Init();
font = TTF_OpenFont("arial.ttf", 12);
fColor.r = 255;
fColor.g = 255;
fColor.b = 255;
}
//Print the designated string at the specified coordinates
void printF(char *c, int x, int y){
fontSurface = TTF_RenderText_Solid(font, c, fColor);
fontRect.x = x;
fontRect.y = y;
SDL_BlitSurface(fontSurface, NULL, screen, &fontRect);
SDL_Flip(screen);
}
int main(int argc, char** argv)
{
// Initialize the SDL library with the Video subsystem
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
//Create the screen
screen = SDL_SetVideoMode(320, 480, 0, SDL_SWSURFACE);
//Initialize fonts
fontInit();
//Print to center of screen
printF("Hello World", screen->w/2 - 11*3, screen->h/2);
do {
// Process the events
while (SDL_PollEvent(&Event)) {
switch (Event.type) {
case SDL_KEYDOWN:
switch (Event.key.keysym.sym) {
// Escape forces us to quit the app
case SDLK_ESCAPE:
Event.type = SDL_QUIT;
break;
default:
break;
}
break;
default:
break;
}
}
SDL_Delay(10);
} while (Event.type != SDL_QUIT);
// Cleanup
SDL_Quit();
return 0;
}