Warning: Undefined array key "HTTP_ACCEPT_LANGUAGE" in /var/www/vhosts/bilgigunlugum.net/httpdocs/index.php on line 43
SDL Game Programming

SDL3 Oyun Programlama ve Godot Oyun Motoru sayfalarımız yayında...

Ana sayfa > Oyun programlama > SDL3 programming > Sound and music

Sound and music

In this section, we will examine a program that moves a texture created from a .png image file and loaded into the main window with the arrow keys and displays the status of these keys in the main window title.

To perform sound operations with SDL3, we need to use the here SDL_mixer library that we installed. For this purpose, we need to perform the following three operations:

  • In the CodeBlocks IDE environment, add the expression -lSDL3_mixer.dll to the "Linker settings" tab of the window opened with the "Project - Build options" menu option
  • Copy the SDL3_mixer.dll file located in the i686-w64-mingw32\bin directory under the directory where the SDL3 library is installed to the directory where the .exe file is located:
  • With the following line of code, include the SDL_image.h header file at the beginning of our code file

    #include <SDL3_mixer/SDL_mixer.h>

When the program is run, in addition to the basic operations, the following operations are performed:

  1. At the beginning of the program, the following global variable is first defined:
    • float x, y: Coordinate variables
    • int button_no: Active button variable
    • Mix_Music *music: Music variable
    • Mix_Chunk *chunk_wav: Wav variable
    • Mix_Chunk *chunk_mp3: Mp3 variable
    • Mix_Chunk *chunk_ogg: Ogg variable
  2. The init_vars() function initializes the button_no global variable.
  3. Within the load_media() function,
    • The menu image is loaded into the texture variable with the IMG_LoadTexture() function.
    • The music.mp3 file is loaded with the Mix_LoadMUS() function.
    • The notification.wav, notification.mp3 and notification.ogg sound files are loaded with the Mix_LoadWAV() function, respectively.
  4. Within the process_event() function, when the mouse movement and click are performed,
    • The mouse coordinates are retrieved with the SDL_GetMouseState) function and assigned to the x and y variables.
    • With the SDL_PointInRect() function, the clicked menu option is determined within the menu texture and the sound file corresponding to this option is played with the Mix_PlayChannel() or Mix_PlayingMusic() functions.
  5. Within the draw_screen() function,
    • With the SDL_RenderTexture() function, the menu texture is copied to the main window.
    • Mouse coordinates, whether the mouse is on the button, the active button value and the active sound file type are shown in the main window bar.

main.c file content will be as follows:


#include <stdio.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h>

// Window width and height
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

// Global variables
int is_running = false;        // Variable that controls the execution of the main loop of the program
SDL_Window *window = NULL;     // Main window variable
SDL_Texture *texture = NULL;   // Texture variable
SDL_Renderer *renderer = NULL; // Renderer variable

float x, y;
int button_no;
Mix_Music *music = NULL;
Mix_Chunk *chunk_wav = NULL;
Mix_Chunk *chunk_mp3 = NULL;
Mix_Chunk *chunk_ogg = NULL;

// Function prototypes
int init_window(void);     // Create window and renderer
void init_vars(void);      // Initialize variables
void load_media(void);     // Load media
void process_event(void);  // Process events
void update_screen();      // Update values
void draw_screen(void);    // Draw screen
void destroy_window(void); // Destroy window

int main(int argc, char* argv[])
{
    // Create window and renderer
    is_running = init_window();

    // Initialize variables
    init_vars();

    // Load image, sound and music
    load_media();

    // Main loop
    while (is_running) {
       process_event(); // Processing SDL events (Here keyboard inputs)
       update_screen(); // Updating variables
       draw_screen();   // Drawing objects on the window (Rendering)
    }

    // Destroy renderer and SDL window
    destroy_window();

    return 0;
}

// Create window and renderer
int init_window(void)
{
    // Initialize the SDL library.
    if(SDL_Init(SDL_INIT_VIDEO) == false) {
       SDL_Log("SDL init error: %s\n", SDL_GetError());
       return false;
    }

    // Create a window and a 2D rendering context for the window.
    if(!SDL_CreateWindowAndRenderer("SDL3 window", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
       return false;
    }

    SDL_AudioSpec audio_spec;

    // Initialize SDL_mixer
    SDL_zero(audio_spec);
    audio_spec.format = MIX_DEFAULT_FORMAT;
    audio_spec.channels = 2;
    audio_spec.freq = 44100;
    if(!Mix_OpenAudio(0, &audio_spec)) {
       SDL_Log("SDL_mixer initialization error: %s\n", SDL_GetError());
       return false;
    }

    return true;
}

// Initialization function that runs only once at the beginning of the program
void init_vars(void)
{
    button_no = 0;
}

// Load image, sound and music
void load_media(void)
{
    // Load image to texture
	texture = IMG_LoadTexture(renderer, "menu_sound.png");

	if(texture == NULL) {
       SDL_Log("IMG_LoadTexture error: %s\n", SDL_GetError());
	}

	// Load music
	music = Mix_LoadMUS("music.mp3");
	if(music == NULL) {
	   SDL_Log( "Load music error%s\n", SDL_GetError());
	}

	chunk_wav = Mix_LoadWAV("notification.wav");
	if(chunk_wav == NULL) {
	   SDL_Log( "Load music error%s\n", SDL_GetError());
	}

	chunk_mp3 = Mix_LoadWAV("notification.mp3");
	if(chunk_mp3 == NULL) {
	   SDL_Log( "Load music error%s\n", SDL_GetError());
	}

	chunk_ogg = Mix_LoadWAV("notification.ogg");
	if(chunk_ogg == NULL) {
	   SDL_Log( "Load music error%s\n", SDL_GetError());
	}
}

// Function to control SDL events and process keyboard inputs
void process_event(void)
{
    SDL_Event event;

    // Creating a loop to process user inputs
    while (SDL_PollEvent(&event)) {
       switch (event.type) {
          case SDL_EVENT_MOUSE_BUTTON_DOWN:
          case SDL_EVENT_MOUSE_MOTION:
          {
               SDL_GetMouseState(&x, &y);
               SDL_Point mouse_pos = { event.motion.x, event.motion.y };
               SDL_Rect rect_menu = { 20, 20, 120, 30 };

               button_no = 0;
               bool lb_state = event.type==SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button==SDL_BUTTON_LEFT;

               // Checking whether the mouse is inside the button
               if(SDL_PointInRect(&mouse_pos, &rect_menu)) {
                  button_no = 1;
                  if(lb_state) Mix_PlayChannel(-1, chunk_wav, 0);
               }
               rect_menu.y = 60;
               if(SDL_PointInRect(&mouse_pos, &rect_menu)) {
                  button_no = 2;
                  if(lb_state) Mix_PlayChannel(-1, chunk_mp3, 0);
               }
               rect_menu.y = 100;
               if(SDL_PointInRect(&mouse_pos, &rect_menu)) {
                  button_no = 3;
                  if(lb_state) Mix_PlayChannel(-1, chunk_ogg, 0);
               }
               rect_menu.y = 140;
               if(SDL_PointInRect(&mouse_pos, &rect_menu)) {
                  button_no = 4;
                  if(lb_state) {
                     if(Mix_PlayingMusic() == 0) {
                        Mix_PlayMusic(music, -1);
                     }
                     else {
                        Mix_HaltMusic();
                     }
                  }
               }

               if(button_no>0) SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER));
               else SDL_SetCursor(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT));
          }
          break;

          case SDL_EVENT_QUIT: // Logout action by the user (x button at the top right of the window)
               is_running = false; // Terminates the execution of the program main loop.
               break;

          case SDL_EVENT_KEY_DOWN: // Key pressed.
               if(event.key.key==SDLK_ESCAPE) {
                  is_running = false;
               }
               break;
       }
    }
}

// Updates objects in the main window
void update_screen(void)
{

}

// Render function used to draw game objects in the main window
void draw_screen(void)
{
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Set the color used for drawing operations.
    SDL_RenderClear(renderer); // Clear the current rendering target with the drawing color.

    // Copy menu texture to main window
    SDL_FRect frect_dst = { 20, 20, 120, 150 };
	SDL_RenderTexture(renderer, texture, NULL, &frect_dst);

    // Loading all objects drawn one by one to the back buffer to the front buffer at once and loading them onto the screen
    // This process prevents each drawn object from being displayed on the screen one by one.
    SDL_RenderPresent(renderer); // Update on screen all operations performed since the previous call

    char cdizi[100];
    char sound_file[5];

    switch(button_no) {
       case 0:
         strcpy(sound_file, "None");
         break;
       case 1:
         strcpy(sound_file, "Wav");
         break;
       case 2:
         strcpy(sound_file, "Mp3");
         break;
       case 3:
         strcpy(sound_file, "Ogg");
         break;
       case 4:
         strcpy(sound_file, "Mp3");
         break;
    }

    sprintf(cdizi, "SDL3 window - Mouse: x: %.2f y: %.2f Mouse in button: %d Source: %s", x, y, button_no, sound_file);
    SDL_SetWindowTitle(window, cdizi);
}

// Destroy Renderer and SDL window, exit from SDL3
void destroy_window(void)
{
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}