Merge branch 'master' into i-cant-think-of-a-name
This commit is contained in:
commit
50786b655b
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # soon
|
||||
github: raysan5
|
||||
patreon: raylib
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: raysan
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
# logic to a self contained function: UpdateDrawFrame(), check core_basic_window_web.c for reference.
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
@ -365,6 +365,7 @@ EXAMPLES = \
|
|||
core/core_window_letterbox \
|
||||
core/core_drop_files \
|
||||
core/core_random_values \
|
||||
core/core_scissor_test \
|
||||
core/core_storage_values \
|
||||
core/core_vr_simulator \
|
||||
core/core_loading_thread \
|
||||
|
|
@ -394,6 +395,7 @@ EXAMPLES = \
|
|||
text/text_rectangle_bounds \
|
||||
text/text_unicode \
|
||||
textures/textures_logo_raylib \
|
||||
textures/textures_mouse_painting \
|
||||
textures/textures_rectangle \
|
||||
textures/textures_srcrec_dstrec \
|
||||
textures/textures_image_drawing \
|
||||
|
|
|
|||
71
examples/core/core_scissor_test.c
Normal file
71
examples/core/core_scissor_test.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [core] example - Scissor test
|
||||
*
|
||||
* This example has been created using raylib 2.5 (www.raylib.com)
|
||||
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
||||
*
|
||||
* Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5)
|
||||
*
|
||||
* Copyright (c) 2019 Chris Dill (@MysteriousSpace)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [core] example - scissor test");
|
||||
|
||||
Rectangle scissorArea = { 0, 0, 300, 300 };
|
||||
bool scissorMode = true;
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyPressed(KEY_S)) scissorMode = !scissorMode;
|
||||
|
||||
// Centre the scissor area around the mouse position
|
||||
scissorArea.x = GetMouseX() - scissorArea.width/2;
|
||||
scissorArea.y = GetMouseY() - scissorArea.height/2;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
if (scissorMode) BeginScissorMode(scissorArea.x, scissorArea.y, scissorArea.width, scissorArea.height);
|
||||
|
||||
// Draw full screen rectangle and some text
|
||||
// NOTE: Only part defined by scissor area will be rendered
|
||||
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), RED);
|
||||
DrawText("Move the mouse around to reveal this text!", 190, 200, 20, LIGHTGRAY);
|
||||
|
||||
if (scissorMode) EndScissorMode();
|
||||
|
||||
DrawRectangleLinesEx(scissorArea, 1, BLACK);
|
||||
DrawText("Press S to toggle scissor test", 10, 10, 20, BLACK);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/core/core_scissor_test.png
Normal file
BIN
examples/core/core_scissor_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -29,7 +29,7 @@ int main(void)
|
|||
int gameScreenWidth = 640;
|
||||
int gameScreenHeight = 480;
|
||||
|
||||
// Render texture initialization
|
||||
// Render texture initialization, used to hold the rendering result so we can easily resize it
|
||||
RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight);
|
||||
SetTextureFilter(target.texture, FILTER_BILINEAR); // Texture scale filter to use
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ int main(void)
|
|||
BeginDrawing();
|
||||
ClearBackground(BLACK);
|
||||
|
||||
// Draw everything in the render texture
|
||||
// Draw everything in the render texture, note this will not be rendered on screen, yet
|
||||
BeginTextureMode(target);
|
||||
|
||||
ClearBackground(RAYWHITE); // Clear render texture background color
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
210
examples/textures/textures_mouse_painting.c
Normal file
210
examples/textures/textures_mouse_painting.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [textures] example - Mouse painting
|
||||
*
|
||||
* This example has been created using raylib 2.5 (www.raylib.com)
|
||||
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
|
||||
*
|
||||
* Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5)
|
||||
*
|
||||
* Copyright (c) 2019 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#define MAX_COLORS_COUNT 23 // Number of colors available
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [textures] example - mouse painting");
|
||||
|
||||
// Colours to choose from
|
||||
Color colors[MAX_COLORS_COUNT] = {
|
||||
RAYWHITE, YELLOW, GOLD, ORANGE, PINK, RED, MAROON, GREEN, LIME, DARKGREEN,
|
||||
SKYBLUE, BLUE, DARKBLUE, PURPLE, VIOLET, DARKPURPLE, BEIGE, BROWN, DARKBROWN,
|
||||
LIGHTGRAY, GRAY, DARKGRAY, BLACK };
|
||||
|
||||
// Define colorsRecs data (for every rectangle)
|
||||
Rectangle colorsRecs[MAX_COLORS_COUNT] = { 0 };
|
||||
|
||||
for (int i = 0; i < MAX_COLORS_COUNT; i++)
|
||||
{
|
||||
colorsRecs[i].x = 10 + 30*i + 2*i;
|
||||
colorsRecs[i].y = 10;
|
||||
colorsRecs[i].width = 30;
|
||||
colorsRecs[i].height = 30;
|
||||
}
|
||||
|
||||
int colorSelected = 0;
|
||||
int colorSelectedPrev = colorSelected;
|
||||
int colorMouseHover = 0;
|
||||
int brushSize = 20;
|
||||
|
||||
Rectangle btnSaveRec = { 750, 10, 40, 30 };
|
||||
bool btnSaveMouseHover = false;
|
||||
bool showSaveMessage = false;
|
||||
int saveMessageCounter = 0;
|
||||
|
||||
// Create a RenderTexture2D to use as a canvas
|
||||
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
|
||||
|
||||
// Clear render texture before entering the game loop
|
||||
BeginTextureMode(target);
|
||||
ClearBackground(colors[0]);
|
||||
EndTextureMode();
|
||||
|
||||
SetTargetFPS(120); // Set our game to run at 120 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
|
||||
// Move between colors with keys
|
||||
if (IsKeyPressed(KEY_RIGHT)) colorSelected++;
|
||||
else if (IsKeyPressed(KEY_LEFT)) colorSelected--;
|
||||
|
||||
if (colorSelected >= MAX_COLORS_COUNT) colorSelected = MAX_COLORS_COUNT - 1;
|
||||
else if (colorSelected < 0) colorSelected = 0;
|
||||
|
||||
// Choose color with mouse
|
||||
for (int i = 0; i < MAX_COLORS_COUNT; i++)
|
||||
{
|
||||
if (CheckCollisionPointRec(mousePos, colorsRecs[i]))
|
||||
{
|
||||
colorMouseHover = i;
|
||||
break;
|
||||
}
|
||||
else colorMouseHover = -1;
|
||||
}
|
||||
|
||||
if ((colorMouseHover >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
|
||||
{
|
||||
colorSelected = colorMouseHover;
|
||||
colorSelectedPrev = colorSelected;
|
||||
}
|
||||
|
||||
// Change brush size
|
||||
brushSize += GetMouseWheelMove()*5;
|
||||
if (brushSize < 2) brushSize = 2;
|
||||
if (brushSize > 50) brushSize = 50;
|
||||
|
||||
if (IsKeyPressed(KEY_C))
|
||||
{
|
||||
// Clear render texture to clear color
|
||||
BeginTextureMode(target);
|
||||
ClearBackground(colors[0]);
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
|
||||
{
|
||||
// Paint circle into render texture
|
||||
// NOTE: To avoid discontinuous circles, we could store
|
||||
// previous-next mouse points and just draw a line using brush size
|
||||
BeginTextureMode(target);
|
||||
if (mousePos.y > 50) DrawCircle(mousePos.x, mousePos.y, brushSize, colors[colorSelected]);
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON))
|
||||
{
|
||||
colorSelected = 0;
|
||||
|
||||
// Erase circle from render texture
|
||||
BeginTextureMode(target);
|
||||
if (mousePos.y > 50) DrawCircle(mousePos.x, mousePos.y, brushSize, colors[0]);
|
||||
EndTextureMode();
|
||||
}
|
||||
else colorSelected = colorSelectedPrev;
|
||||
|
||||
// Check mouse hover save button
|
||||
if (CheckCollisionPointRec(mousePos, btnSaveRec)) btnSaveMouseHover = true;
|
||||
else btnSaveMouseHover = false;
|
||||
|
||||
// Image saving logic
|
||||
// NOTE: Saving painted texture to a default named image
|
||||
if ((btnSaveMouseHover && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) || IsKeyPressed(KEY_S))
|
||||
{
|
||||
Image image = GetTextureData(target.texture);
|
||||
ImageFlipVertical(&image);
|
||||
ExportImage(image, "my_amazing_texture_painting.png");
|
||||
UnloadImage(image);
|
||||
showSaveMessage = true;
|
||||
}
|
||||
|
||||
if (showSaveMessage)
|
||||
{
|
||||
// On saving, show a full screen message for 2 seconds
|
||||
saveMessageCounter++;
|
||||
if (saveMessageCounter > 240)
|
||||
{
|
||||
showSaveMessage = false;
|
||||
saveMessageCounter = 0;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
|
||||
DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, (Vector2){ 0, 0 }, WHITE);
|
||||
|
||||
// Draw drawing circle for reference
|
||||
if (mousePos.y > 50)
|
||||
{
|
||||
if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) DrawCircleLines(mousePos.x, mousePos.y, brushSize, colors[colorSelected]);
|
||||
else DrawCircle(GetMouseX(), GetMouseY(), brushSize, colors[colorSelected]);
|
||||
}
|
||||
|
||||
// Draw top panel
|
||||
DrawRectangle(0, 0, GetScreenWidth(), 50, RAYWHITE);
|
||||
DrawLine(0, 50, GetScreenWidth(), 50, LIGHTGRAY);
|
||||
|
||||
// Draw color selection rectangles
|
||||
for (int i = 0; i < MAX_COLORS_COUNT; i++) DrawRectangleRec(colorsRecs[i], colors[i]);
|
||||
DrawRectangleLines(10, 10, 30, 30, LIGHTGRAY);
|
||||
|
||||
if (colorMouseHover >= 0) DrawRectangleRec(colorsRecs[colorMouseHover], Fade(WHITE, 0.6f));
|
||||
|
||||
DrawRectangleLinesEx((Rectangle){ colorsRecs[colorSelected].x - 2, colorsRecs[colorSelected].y - 2,
|
||||
colorsRecs[colorSelected].width + 4, colorsRecs[colorSelected].height + 4 }, 2, BLACK);
|
||||
|
||||
// Draw save image button
|
||||
DrawRectangleLinesEx(btnSaveRec, 2, btnSaveMouseHover? RED : BLACK);
|
||||
DrawText("SAVE!", 755, 20, 10, btnSaveMouseHover? RED : BLACK);
|
||||
|
||||
// Draw save image message
|
||||
if (showSaveMessage)
|
||||
{
|
||||
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f));
|
||||
DrawRectangle(0, 150, GetScreenWidth(), 80, BLACK);
|
||||
DrawText("IMAGE SAVED: my_amazing_texture_painting.png", 150, 180, 20, RAYWHITE);
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
UnloadRenderTexture(target); // Unload render texture
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/textures/textures_mouse_painting.png
Normal file
BIN
examples/textures/textures_mouse_painting.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP)
|
|||
ifeq ($(PLATFORM_OS),WINDOWS)
|
||||
# resource file contains windows executable icon and properties
|
||||
# -Wl,--subsystem,windows hides the console window
|
||||
CFLAGS += $(RAYLIB_PATH)/raylib.rc.data -Wl,--subsystem,windows
|
||||
CFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data -Wl,--subsystem,windows
|
||||
endif
|
||||
ifeq ($(PLATFORM_OS),LINUX)
|
||||
ifeq ($(RAYLIB_LIBTYPE),STATIC)
|
||||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ int main(void)
|
|||
atlas02 = LoadTexture("resources/graphics/atlas02.png");
|
||||
|
||||
#if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID)
|
||||
colorBlend = LoadShader("resources/shaders/glsl100/base.vs", "resources/shaders/glsl100/blend_color.fs");
|
||||
colorBlend = LoadShader(0, "resources/shaders/glsl100/blend_color.fs");
|
||||
#else
|
||||
colorBlend = LoadShader("resources/shaders/glsl330/base.vs", "resources/shaders/glsl330/blend_color.fs");
|
||||
colorBlend = LoadShader(0, "resources/shaders/glsl330/blend_color.fs");
|
||||
#endif
|
||||
|
||||
InitAudioDevice();
|
||||
|
|
@ -76,20 +76,14 @@ int main(void)
|
|||
fxDieDingo = LoadSound("resources/audio/dingo_die.ogg");
|
||||
fxDieOwl = LoadSound("resources/audio/owl_die.ogg");
|
||||
|
||||
|
||||
music = LoadMusicStream("resources/audio/jngl.xm");
|
||||
PlayMusicStream(music);
|
||||
SetMusicVolume(music, 1.0f);
|
||||
SetMusicVolume(music, 2.0f);
|
||||
|
||||
// Define and init first screen
|
||||
// NOTE: currentScreen is defined in screens.h as a global variable
|
||||
currentScreen = TITLE;
|
||||
|
||||
InitLogoScreen();
|
||||
//InitOptionsScreen();
|
||||
InitTitleScreen();
|
||||
InitGameplayScreen();
|
||||
InitEndingScreen();
|
||||
|
||||
#if defined(PLATFORM_WEB)
|
||||
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
|
||||
|
|
@ -258,8 +252,6 @@ void UpdateDrawFrame(void)
|
|||
|
||||
if (onTransition) DrawTransition();
|
||||
|
||||
DrawFPS(20, GetScreenHeight() - 30);
|
||||
|
||||
DrawRectangle(GetScreenWidth() - 200, GetScreenHeight() - 50, 200, 40, Fade(WHITE, 0.6f));
|
||||
DrawText("ALPHA VERSION", GetScreenWidth() - 180, GetScreenHeight() - 40, 20, DARKGRAY);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
//#define DEBUG
|
||||
|
||||
// DONE: Review MAX_* limits, don't waste memory!!!
|
||||
#define MAX_ENEMIES 16
|
||||
#define MAX_BAMBOO 16
|
||||
#define MAX_LEAVES 14
|
||||
|
|
@ -343,7 +342,6 @@ static Rectangle leftButton = {0, 0, 0, 0};
|
|||
static Rectangle rightButton = {0, 0, 0, 0};
|
||||
static Rectangle powerButton = {0, 0, 0, 0};
|
||||
static Rectangle fire[MAX_FIRE];
|
||||
//static Rectangle flames[MAX_FLAMES];
|
||||
static Rectangle ice[MAX_ICE];
|
||||
static Rectangle resin[MAX_RESIN];
|
||||
static Rectangle wind[MAX_WIND];
|
||||
|
|
@ -351,7 +349,7 @@ static Rectangle bamboo[MAX_BAMBOO];
|
|||
static Rectangle snake[MAX_ENEMIES];
|
||||
static Rectangle dingo[MAX_ENEMIES];
|
||||
static Rectangle owl[MAX_ENEMIES];
|
||||
static Rectangle leaf[MAX_LEAVES]; // DONE: Review name!
|
||||
static Rectangle leaf[MAX_LEAVES];
|
||||
static Rectangle powerBar;
|
||||
static Rectangle backBar;
|
||||
static Rectangle fireAnimation;
|
||||
|
|
@ -387,7 +385,7 @@ static Vector2 textSize;
|
|||
static Vector2 clockPosition;
|
||||
|
||||
static Particle enemyHit[MAX_ENEMIES];
|
||||
static ParticleSystem leafParticles[MAX_LEAVES]; // DONE: Review!!! Creating 40 ParticleSystem!!! -> 40*128 = 5120 Particles! Maybe better create a struct Leaf?
|
||||
static ParticleSystem leafParticles[MAX_LEAVES];
|
||||
static ParticleSystem snowParticle;
|
||||
static ParticleSystem backSnowParticle;
|
||||
static ParticleSystem dandelionParticle;
|
||||
|
|
@ -1043,12 +1041,8 @@ void UpdateGameplayScreen(void)
|
|||
#endif
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
if (currentLeaves < LEAVESTOTRANSFORM && (IsKeyPressed(KEY_ENTER)))
|
||||
{
|
||||
currentLeaves += LEAVESTOTRANSFORM;
|
||||
}
|
||||
if ((currentLeaves < LEAVESTOTRANSFORM) && (IsKeyPressed(KEY_ENTER))) currentLeaves += LEAVESTOTRANSFORM;
|
||||
#endif
|
||||
|
||||
if (coolDown)
|
||||
{
|
||||
power += 20;
|
||||
|
|
@ -1427,10 +1421,6 @@ void UpdateGameplayScreen(void)
|
|||
|
||||
if (CheckCollisionRecs(player, leaf[j]) && leafActive[j])
|
||||
{
|
||||
//power += 20;
|
||||
//printf("coin %c", coinType[j]);
|
||||
|
||||
// DONE: Review
|
||||
popupLeaves[j].position = (Vector2){ leaf[j].x, leaf[j].y };
|
||||
popupLeaves[j].scale = 1.0f;
|
||||
popupLeaves[j].alpha = 1.0f;
|
||||
|
|
@ -2232,7 +2222,6 @@ void UpdateGameplayScreen(void)
|
|||
player.x -= speed;
|
||||
grabCounter += 1*TIME_FACTOR;
|
||||
|
||||
// DONE: Review, before checking collision with ALL enemies, check if they are active!
|
||||
for (int i = 0; i < MAX_ENEMIES; i++)
|
||||
{
|
||||
if (CheckCollisionRecs(player, snake[i]) && !isHitSnake[i] && snakeActive[i])
|
||||
|
|
@ -2286,7 +2275,6 @@ void UpdateGameplayScreen(void)
|
|||
enemyHit[i].speed = (Vector2){ dingo[i].x, dingo[i].y };
|
||||
enemyHit[i].size = (float)GetRandomValue(5, 10)/30;
|
||||
enemyHit[i].rotation = 0.0f;
|
||||
//enemyHit[i].color = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 };
|
||||
enemyHit[i].alpha = 1.0f;
|
||||
enemyHit[i].active = true;
|
||||
|
||||
|
|
@ -2317,7 +2305,6 @@ void UpdateGameplayScreen(void)
|
|||
enemyHit[i].speed = (Vector2){ owl[i].x, owl[i].y };
|
||||
enemyHit[i].size = (float)GetRandomValue(5, 10)/30;
|
||||
enemyHit[i].rotation = 0.0f;
|
||||
//enemyHit[i].color = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 };
|
||||
enemyHit[i].alpha = 1.0f;
|
||||
enemyHit[i].active = true;
|
||||
|
||||
|
|
@ -2392,8 +2379,6 @@ void UpdateGameplayScreen(void)
|
|||
thisFrameKoala = 0;
|
||||
}
|
||||
|
||||
//if (curFrameKoala > 1) curFrameKoala = ;
|
||||
|
||||
if (curFrameKoala <= 1) koalaAnimationTransform.x = gameplay_koala_transform.x + koalaAnimationTransform.width*curFrameKoala;
|
||||
|
||||
if (transAniCounter >= 5)
|
||||
|
|
@ -2407,8 +2392,7 @@ void UpdateGameplayScreen(void)
|
|||
finalColor = RED;
|
||||
finalColor2 = WHITE;
|
||||
}
|
||||
|
||||
if (!transBackAnim)
|
||||
else
|
||||
{
|
||||
finalColor = WHITE;
|
||||
finalColor2 = RED;
|
||||
|
|
@ -2420,9 +2404,7 @@ void UpdateGameplayScreen(void)
|
|||
thisFrameKoala = 0;
|
||||
curFrameKoala = 0;
|
||||
speedFX.active = true;
|
||||
//speedMod = 2;
|
||||
transCount = 0;
|
||||
//printf ("THIS ISN'T EVEN MY FINAL FORM");
|
||||
bambooTimer += 15*TIME_FACTOR;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
"PLATFORM_DESKTOP"
|
||||
],
|
||||
"compilerPath": "C:/raylib/mingw/bin/gcc.exe",
|
||||
"cStandard": "c11",
|
||||
"cStandard": "c99",
|
||||
"cppStandard": "c++14",
|
||||
"intelliSenseMode": "clang-x64"
|
||||
"intelliSenseMode": "gcc-x64"
|
||||
},
|
||||
{
|
||||
"name": "Mac",
|
||||
|
|
|
|||
10
projects/VSCode/.vscode/tasks.json
vendored
10
projects/VSCode/.vscode/tasks.json
vendored
|
|
@ -26,7 +26,10 @@
|
|||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "build release",
|
||||
|
|
@ -47,7 +50,10 @@
|
|||
"RAYLIB_PATH=<path_to_raylib>/raylib",
|
||||
],
|
||||
},
|
||||
"group": "build"
|
||||
"group": "build",
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ RAYLIB_VERSION ?= 2.5.0
|
|||
RAYLIB_API_VERSION ?= 251
|
||||
RAYLIB_PATH ?= ..\..
|
||||
|
||||
# Define default options
|
||||
# Define compiler path on Windows
|
||||
COMPILER_PATH ?= C:/raylib/mingw/bin
|
||||
|
||||
# Define default options
|
||||
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB
|
||||
PLATFORM ?= PLATFORM_DESKTOP
|
||||
|
||||
|
|
@ -68,6 +70,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP)
|
|||
# ifeq ($(UNAME),Msys) -> Windows
|
||||
ifeq ($(OS),Windows_NT)
|
||||
PLATFORM_OS=WINDOWS
|
||||
export PATH := $(COMPILER_PATH):$(PATH)
|
||||
else
|
||||
UNAMEOS=$(shell uname)
|
||||
ifeq ($(UNAMEOS),Linux)
|
||||
|
|
@ -236,7 +239,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -251,10 +251,6 @@ void SetCameraMode(Camera camera, int mode)
|
|||
cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
|
||||
cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW)
|
||||
|
||||
// NOTE: Just testing what cameraAngle means
|
||||
//cameraAngle.x = 0.0f*DEG2RAD; // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
|
||||
//cameraAngle.y = -60.0f*DEG2RAD; // Camera angle in plane XY (0 aligned with X, move positive CW)
|
||||
|
||||
playerEyesPosition = camera.position.y;
|
||||
|
||||
// Lock cursor for first person and third person cameras
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@
|
|||
#include "gestures.h" // Gestures detection functionality
|
||||
#endif
|
||||
|
||||
#if defined(SUPPORT_CAMERA_SYSTEM) && !defined(PLATFORM_ANDROID)
|
||||
#if defined(SUPPORT_CAMERA_SYSTEM)
|
||||
#define CAMERA_IMPLEMENTATION
|
||||
#include "camera.h" // Camera system functionality
|
||||
#endif
|
||||
|
|
|
|||
458
src/raudio.c
458
src/raudio.c
|
|
@ -236,16 +236,16 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
|
|||
// AudioBuffer management functions declaration
|
||||
// NOTE: Those functions are not exposed by raylib... for the moment
|
||||
AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage);
|
||||
void CloseAudioBuffer(AudioBuffer *audioBuffer);
|
||||
bool IsAudioBufferPlaying(AudioBuffer *audioBuffer);
|
||||
void PlayAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void StopAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void PauseAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void ResumeAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume);
|
||||
void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch);
|
||||
void TrackAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void UntrackAudioBuffer(AudioBuffer *audioBuffer);
|
||||
void CloseAudioBuffer(AudioBuffer *buffer);
|
||||
bool IsAudioBufferPlaying(AudioBuffer *buffer);
|
||||
void PlayAudioBuffer(AudioBuffer *buffer);
|
||||
void StopAudioBuffer(AudioBuffer *buffer);
|
||||
void PauseAudioBuffer(AudioBuffer *buffer);
|
||||
void ResumeAudioBuffer(AudioBuffer *buffer);
|
||||
void SetAudioBufferVolume(AudioBuffer *buffer, float volume);
|
||||
void SetAudioBufferPitch(AudioBuffer *buffer, float pitch);
|
||||
void TrackAudioBuffer(AudioBuffer *buffer);
|
||||
void UntrackAudioBuffer(AudioBuffer *buffer);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Multi channel playback globals
|
||||
|
|
@ -644,151 +644,127 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
|
|||
}
|
||||
|
||||
// Delete an audio buffer
|
||||
void CloseAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void CloseAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
if (buffer != NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio buffer");
|
||||
return;
|
||||
UntrackAudioBuffer(buffer);
|
||||
RL_FREE(buffer->buffer);
|
||||
RL_FREE(buffer);
|
||||
}
|
||||
|
||||
UntrackAudioBuffer(audioBuffer);
|
||||
RL_FREE(audioBuffer->buffer);
|
||||
RL_FREE(audioBuffer);
|
||||
else TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio buffer");
|
||||
}
|
||||
|
||||
// Check if an audio buffer is playing
|
||||
bool IsAudioBufferPlaying(AudioBuffer *audioBuffer)
|
||||
bool IsAudioBufferPlaying(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer");
|
||||
return false;
|
||||
}
|
||||
bool result = false;
|
||||
|
||||
return audioBuffer->playing && !audioBuffer->paused;
|
||||
if (buffer != NULL) result = (buffer->playing && !buffer->paused);
|
||||
else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Play an audio buffer
|
||||
// NOTE: Buffer is restarted to the start.
|
||||
// Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained.
|
||||
void PlayAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void PlayAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
if (buffer != NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer");
|
||||
return;
|
||||
buffer->playing = true;
|
||||
buffer->paused = false;
|
||||
buffer->frameCursorPos = 0;
|
||||
}
|
||||
|
||||
audioBuffer->playing = true;
|
||||
audioBuffer->paused = false;
|
||||
audioBuffer->frameCursorPos = 0;
|
||||
else TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer");
|
||||
}
|
||||
|
||||
// Stop an audio buffer
|
||||
void StopAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void StopAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
if (buffer != NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "StopAudioBuffer() : No audio buffer");
|
||||
return;
|
||||
if (IsAudioBufferPlaying(buffer))
|
||||
{
|
||||
buffer->playing = false;
|
||||
buffer->paused = false;
|
||||
buffer->frameCursorPos = 0;
|
||||
buffer->isSubBufferProcessed[0] = true;
|
||||
buffer->isSubBufferProcessed[1] = true;
|
||||
}
|
||||
|
||||
// Don't do anything if the audio buffer is already stopped.
|
||||
if (!IsAudioBufferPlaying(audioBuffer)) return;
|
||||
|
||||
audioBuffer->playing = false;
|
||||
audioBuffer->paused = false;
|
||||
audioBuffer->frameCursorPos = 0;
|
||||
audioBuffer->isSubBufferProcessed[0] = true;
|
||||
audioBuffer->isSubBufferProcessed[1] = true;
|
||||
}
|
||||
else TraceLog(LOG_ERROR, "StopAudioBuffer() : No audio buffer");
|
||||
}
|
||||
|
||||
// Pause an audio buffer
|
||||
void PauseAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void PauseAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
audioBuffer->paused = true;
|
||||
if (buffer != NULL) buffer->paused = true;
|
||||
else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer");
|
||||
}
|
||||
|
||||
// Resume an audio buffer
|
||||
void ResumeAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void ResumeAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "ResumeAudioBuffer() : No audio buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
audioBuffer->paused = false;
|
||||
if (buffer != NULL) buffer->paused = false;
|
||||
else TraceLog(LOG_ERROR, "ResumeAudioBuffer() : No audio buffer");
|
||||
}
|
||||
|
||||
// Set volume for an audio buffer
|
||||
void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume)
|
||||
void SetAudioBufferVolume(AudioBuffer *buffer, float volume)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
TraceLog(LOG_WARNING, "SetAudioBufferVolume() : No audio buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
audioBuffer->volume = volume;
|
||||
if (buffer != NULL) buffer->volume = volume;
|
||||
else TraceLog(LOG_WARNING, "SetAudioBufferVolume() : No audio buffer");
|
||||
}
|
||||
|
||||
// Set pitch for an audio buffer
|
||||
void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
|
||||
void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
|
||||
{
|
||||
if (audioBuffer == NULL)
|
||||
if (buffer != NULL)
|
||||
{
|
||||
TraceLog(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
float pitchMul = pitch/audioBuffer->pitch;
|
||||
float pitchMul = pitch/buffer->pitch;
|
||||
|
||||
// Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches
|
||||
// will make the sound faster; lower pitches make it slower.
|
||||
ma_uint32 newOutputSampleRate = (ma_uint32)((float)audioBuffer->dsp.src.config.sampleRateOut / pitchMul);
|
||||
audioBuffer->pitch *= (float)audioBuffer->dsp.src.config.sampleRateOut / newOutputSampleRate;
|
||||
ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul);
|
||||
buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate;
|
||||
|
||||
ma_pcm_converter_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate);
|
||||
ma_pcm_converter_set_output_sample_rate(&buffer->dsp, newOutputSampleRate);
|
||||
}
|
||||
else TraceLog(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer");
|
||||
}
|
||||
|
||||
// Track audio buffer to linked list next position
|
||||
void TrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void TrackAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
ma_mutex_lock(&audioLock);
|
||||
{
|
||||
if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer;
|
||||
if (firstAudioBuffer == NULL) firstAudioBuffer = buffer;
|
||||
else
|
||||
{
|
||||
lastAudioBuffer->next = audioBuffer;
|
||||
audioBuffer->prev = lastAudioBuffer;
|
||||
lastAudioBuffer->next = buffer;
|
||||
buffer->prev = lastAudioBuffer;
|
||||
}
|
||||
|
||||
lastAudioBuffer = audioBuffer;
|
||||
lastAudioBuffer = buffer;
|
||||
}
|
||||
ma_mutex_unlock(&audioLock);
|
||||
}
|
||||
|
||||
// Untrack audio buffer from linked list
|
||||
void UntrackAudioBuffer(AudioBuffer *audioBuffer)
|
||||
void UntrackAudioBuffer(AudioBuffer *buffer)
|
||||
{
|
||||
ma_mutex_lock(&audioLock);
|
||||
{
|
||||
if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next;
|
||||
else audioBuffer->prev->next = audioBuffer->next;
|
||||
if (buffer->prev == NULL) firstAudioBuffer = buffer->next;
|
||||
else buffer->prev->next = buffer->next;
|
||||
|
||||
if (audioBuffer->next == NULL) lastAudioBuffer = audioBuffer->prev;
|
||||
else audioBuffer->next->prev = audioBuffer->prev;
|
||||
if (buffer->next == NULL) lastAudioBuffer = buffer->prev;
|
||||
else buffer->next->prev = buffer->prev;
|
||||
|
||||
audioBuffer->prev = NULL;
|
||||
audioBuffer->next = NULL;
|
||||
buffer->prev = NULL;
|
||||
buffer->next = NULL;
|
||||
}
|
||||
ma_mutex_unlock(&audioLock);
|
||||
}
|
||||
|
|
@ -802,10 +778,9 @@ Wave LoadWave(const char *fileName)
|
|||
{
|
||||
Wave wave = { 0 };
|
||||
|
||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||
if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
|
||||
#else
|
||||
if (false) { }
|
||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||
else if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
|
||||
|
|
@ -821,25 +796,6 @@ Wave LoadWave(const char *fileName)
|
|||
return wave;
|
||||
}
|
||||
|
||||
// Load wave data from raw array data
|
||||
Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels)
|
||||
{
|
||||
Wave wave;
|
||||
|
||||
wave.data = data;
|
||||
wave.sampleCount = sampleCount;
|
||||
wave.sampleRate = sampleRate;
|
||||
wave.sampleSize = sampleSize;
|
||||
wave.channels = channels;
|
||||
|
||||
// NOTE: Copy wave data to work with, user is responsible of input data to free
|
||||
Wave cwave = WaveCopy(wave);
|
||||
|
||||
WaveFormat(&cwave, sampleRate, sampleSize, channels);
|
||||
|
||||
return cwave;
|
||||
}
|
||||
|
||||
// Load sound from file
|
||||
// NOTE: The entire file is loaded to memory to be played (no-streaming)
|
||||
Sound LoadSound(const char *fileName)
|
||||
|
|
@ -903,7 +859,7 @@ void UnloadWave(Wave wave)
|
|||
// Unload sound
|
||||
void UnloadSound(Sound sound)
|
||||
{
|
||||
CloseAudioBuffer((AudioBuffer *)sound.stream.buffer);
|
||||
CloseAudioBuffer(sound.stream.buffer);
|
||||
|
||||
TraceLog(LOG_INFO, "Unloaded sound data from RAM");
|
||||
}
|
||||
|
|
@ -911,7 +867,7 @@ void UnloadSound(Sound sound)
|
|||
// Update sound buffer with new data
|
||||
void UpdateSound(Sound sound, const void *data, int samplesCount)
|
||||
{
|
||||
AudioBuffer *audioBuffer = (AudioBuffer *)sound.stream.buffer;
|
||||
AudioBuffer *audioBuffer = sound.stream.buffer;
|
||||
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
|
|
@ -930,10 +886,9 @@ void ExportWave(Wave wave, const char *fileName)
|
|||
{
|
||||
bool success = false;
|
||||
|
||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
||||
#else
|
||||
if (false) { }
|
||||
#if defined(SUPPORT_FILEFORMAT_WAV)
|
||||
else if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
|
||||
#endif
|
||||
else if (IsFileExtension(fileName, ".raw"))
|
||||
{
|
||||
|
|
@ -994,7 +949,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
|
|||
// Play a sound
|
||||
void PlaySound(Sound sound)
|
||||
{
|
||||
PlayAudioBuffer((AudioBuffer *)sound.stream.buffer);
|
||||
PlayAudioBuffer(sound.stream.buffer);
|
||||
}
|
||||
|
||||
// Play a sound in the multichannel buffer pool
|
||||
|
|
@ -1046,17 +1001,16 @@ void PlaySoundMulti(Sound sound)
|
|||
audioBufferPoolChannels[index] = audioBufferPoolCounter;
|
||||
audioBufferPoolCounter++;
|
||||
|
||||
audioBufferPool[index]->volume = ((AudioBuffer*)sound.stream.buffer)->volume;
|
||||
audioBufferPool[index]->pitch = ((AudioBuffer*)sound.stream.buffer)->pitch;
|
||||
audioBufferPool[index]->looping = ((AudioBuffer*)sound.stream.buffer)->looping;
|
||||
audioBufferPool[index]->usage = ((AudioBuffer*)sound.stream.buffer)->usage;
|
||||
audioBufferPool[index]->volume = sound.stream.buffer->volume;
|
||||
audioBufferPool[index]->pitch = sound.stream.buffer->pitch;
|
||||
audioBufferPool[index]->looping = sound.stream.buffer->looping;
|
||||
audioBufferPool[index]->usage = sound.stream.buffer->usage;
|
||||
audioBufferPool[index]->isSubBufferProcessed[0] = false;
|
||||
audioBufferPool[index]->isSubBufferProcessed[1] = false;
|
||||
audioBufferPool[index]->bufferSizeInFrames = ((AudioBuffer*)sound.stream.buffer)->bufferSizeInFrames;
|
||||
audioBufferPool[index]->buffer = ((AudioBuffer*)sound.stream.buffer)->buffer;
|
||||
audioBufferPool[index]->bufferSizeInFrames = sound.stream.buffer->bufferSizeInFrames;
|
||||
audioBufferPool[index]->buffer = sound.stream.buffer->buffer;
|
||||
|
||||
PlayAudioBuffer(audioBufferPool[index]);
|
||||
|
||||
}
|
||||
|
||||
// Stop any sound played with PlaySoundMulti()
|
||||
|
|
@ -1081,37 +1035,37 @@ int GetSoundsPlaying(void)
|
|||
// Pause a sound
|
||||
void PauseSound(Sound sound)
|
||||
{
|
||||
PauseAudioBuffer((AudioBuffer *)sound.stream.buffer);
|
||||
PauseAudioBuffer(sound.stream.buffer);
|
||||
}
|
||||
|
||||
// Resume a paused sound
|
||||
void ResumeSound(Sound sound)
|
||||
{
|
||||
ResumeAudioBuffer((AudioBuffer *)sound.stream.buffer);
|
||||
ResumeAudioBuffer(sound.stream.buffer);
|
||||
}
|
||||
|
||||
// Stop reproducing a sound
|
||||
void StopSound(Sound sound)
|
||||
{
|
||||
StopAudioBuffer((AudioBuffer *)sound.stream.buffer);
|
||||
StopAudioBuffer(sound.stream.buffer);
|
||||
}
|
||||
|
||||
// Check if a sound is playing
|
||||
bool IsSoundPlaying(Sound sound)
|
||||
{
|
||||
return IsAudioBufferPlaying((AudioBuffer *)sound.stream.buffer);
|
||||
return IsAudioBufferPlaying(sound.stream.buffer);
|
||||
}
|
||||
|
||||
// Set volume for a sound
|
||||
void SetSoundVolume(Sound sound, float volume)
|
||||
{
|
||||
SetAudioBufferVolume((AudioBuffer *)sound.stream.buffer, volume);
|
||||
SetAudioBufferVolume(sound.stream.buffer, volume);
|
||||
}
|
||||
|
||||
// Set pitch for a sound
|
||||
void SetSoundPitch(Sound sound, float pitch)
|
||||
{
|
||||
SetAudioBufferPitch((AudioBuffer *)sound.stream.buffer, pitch);
|
||||
SetAudioBufferPitch(sound.stream.buffer, pitch);
|
||||
}
|
||||
|
||||
// Convert wave data to desired format
|
||||
|
|
@ -1212,53 +1166,52 @@ float *GetWaveData(Wave wave)
|
|||
// Load music stream from file
|
||||
Music LoadMusicStream(const char *fileName)
|
||||
{
|
||||
Music music = (MusicStream *)RL_MALLOC(sizeof(MusicStream));
|
||||
bool musicLoaded = true;
|
||||
Music music = { 0 };
|
||||
bool musicLoaded = false;
|
||||
|
||||
if (false) { }
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
if (IsFileExtension(fileName, ".ogg"))
|
||||
else if (IsFileExtension(fileName, ".ogg"))
|
||||
{
|
||||
// Open ogg audio stream
|
||||
music->ctxData = stb_vorbis_open_filename(fileName, NULL, NULL);
|
||||
music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL);
|
||||
|
||||
if (music->ctxData == NULL) musicLoaded = false;
|
||||
else
|
||||
if (music.ctxData != NULL)
|
||||
{
|
||||
stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music->ctxData); // Get Ogg file info
|
||||
music.ctxType = MUSIC_AUDIO_OGG;
|
||||
stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info
|
||||
|
||||
// OGG bit rate defaults to 16 bit, it's enough for compressed format
|
||||
music->stream = InitAudioStream(info.sample_rate, 16, info.channels);
|
||||
music->sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music->ctxData)*info.channels;
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music->ctxType = MUSIC_AUDIO_OGG;
|
||||
music->loopCount = 0; // Infinite loop by default
|
||||
music.stream = InitAudioStream(info.sample_rate, 16, info.channels);
|
||||
music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels;
|
||||
music.sampleLeft = music.sampleCount;
|
||||
music.loopCount = 0; // Infinite loop by default
|
||||
musicLoaded = true;
|
||||
|
||||
TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music->sampleCount);
|
||||
TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music.sampleCount);
|
||||
TraceLog(LOG_DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate);
|
||||
TraceLog(LOG_DEBUG, "[%s] OGG channels: %i", fileName, info.channels);
|
||||
TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (false) {}
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||
else if (IsFileExtension(fileName, ".flac"))
|
||||
{
|
||||
music->ctxData = drflac_open_file(fileName);
|
||||
music.ctxData = drflac_open_file(fileName);
|
||||
|
||||
if (music->ctxData == NULL) musicLoaded = false;
|
||||
else
|
||||
if (music.ctxData != NULL)
|
||||
{
|
||||
drflac *ctxFlac = (drflac *)music->ctxData;
|
||||
music.ctxType = MUSIC_AUDIO_FLAC;
|
||||
drflac *ctxFlac = (drflac *)music.ctxData;
|
||||
|
||||
music->stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
||||
music->sampleCount = (unsigned int)ctxFlac->totalSampleCount;
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music->ctxType = MUSIC_AUDIO_FLAC;
|
||||
music->loopCount = 0; // Infinite loop by default
|
||||
music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels);
|
||||
music.sampleCount = (unsigned int)ctxFlac->totalSampleCount;
|
||||
music.sampleLeft = music.sampleCount;
|
||||
music.loopCount = 0; // Infinite loop by default
|
||||
musicLoaded = true;
|
||||
|
||||
TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music->sampleCount);
|
||||
TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music.sampleCount);
|
||||
TraceLog(LOG_DEBUG, "[%s] FLAC sample rate: %i", fileName, ctxFlac->sampleRate);
|
||||
TraceLog(LOG_DEBUG, "[%s] FLAC bits per sample: %i", fileName, ctxFlac->bitsPerSample);
|
||||
TraceLog(LOG_DEBUG, "[%s] FLAC channels: %i", fileName, ctxFlac->channels);
|
||||
|
|
@ -1269,24 +1222,24 @@ Music LoadMusicStream(const char *fileName)
|
|||
else if (IsFileExtension(fileName, ".mp3"))
|
||||
{
|
||||
drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3));
|
||||
music->ctxData = ctxMp3;
|
||||
music.ctxData = ctxMp3;
|
||||
|
||||
int result = drmp3_init_file(ctxMp3, fileName, NULL);
|
||||
|
||||
if (!result) musicLoaded = false;
|
||||
else
|
||||
if (result > 0)
|
||||
{
|
||||
music.ctxType = MUSIC_AUDIO_MP3;
|
||||
|
||||
music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
|
||||
music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
|
||||
music.sampleLeft = music.sampleCount;
|
||||
music.loopCount = 0; // Infinite loop by default
|
||||
musicLoaded = true;
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, ctxMp3->sampleRate);
|
||||
TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32);
|
||||
TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, ctxMp3->channels);
|
||||
|
||||
music->stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels);
|
||||
music->sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels;
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music->ctxType = MUSIC_AUDIO_MP3;
|
||||
music->loopCount = 0; // Infinite loop by default
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->sampleCount);
|
||||
TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music.sampleCount);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1297,73 +1250,70 @@ Music LoadMusicStream(const char *fileName)
|
|||
|
||||
int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName);
|
||||
|
||||
if (!result) // XM context created successfully
|
||||
if (result > 0) // XM context created successfully
|
||||
{
|
||||
music.ctxType = MUSIC_MODULE_XM;
|
||||
jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops
|
||||
|
||||
// NOTE: Only stereo is supported for XM
|
||||
music->stream = InitAudioStream(48000, 16, 2);
|
||||
music->sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm);
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music->ctxType = MUSIC_MODULE_XM;
|
||||
music->loopCount = 0; // Infinite loop by default
|
||||
music.stream = InitAudioStream(48000, 16, 2);
|
||||
music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm);
|
||||
music.sampleLeft = music.sampleCount;
|
||||
music.loopCount = 0; // Infinite loop by default
|
||||
musicLoaded = true;
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music->sampleCount);
|
||||
TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music->sampleCount/48000.0f);
|
||||
music.ctxData = ctxXm;
|
||||
|
||||
music->ctxData = ctxXm;
|
||||
TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music.sampleCount);
|
||||
TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f);
|
||||
}
|
||||
else musicLoaded = false;
|
||||
}
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
else if (IsFileExtension(fileName, ".mod"))
|
||||
{
|
||||
jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t));
|
||||
music->ctxData = ctxMod;
|
||||
music.ctxData = ctxMod;
|
||||
|
||||
jar_mod_init(ctxMod);
|
||||
int result = jar_mod_load_file(ctxMod, fileName);
|
||||
|
||||
if (jar_mod_load_file(ctxMod, fileName))
|
||||
if (result > 0)
|
||||
{
|
||||
// NOTE: Only stereo is supported for MOD
|
||||
music->stream = InitAudioStream(48000, 16, 2);
|
||||
music->sampleCount = (unsigned int)jar_mod_max_samples(ctxMod);
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music->ctxType = MUSIC_MODULE_MOD;
|
||||
music->loopCount = 0; // Infinite loop by default
|
||||
music.ctxType = MUSIC_MODULE_MOD;
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music->sampleLeft);
|
||||
TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->sampleCount/48000.0f);
|
||||
// NOTE: Only stereo is supported for MOD
|
||||
music.stream = InitAudioStream(48000, 16, 2);
|
||||
music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod);
|
||||
music.sampleLeft = music.sampleCount;
|
||||
music.loopCount = 0; // Infinite loop by default
|
||||
musicLoaded = true;
|
||||
|
||||
TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music.sampleLeft);
|
||||
TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f);
|
||||
}
|
||||
else musicLoaded = false;
|
||||
}
|
||||
#endif
|
||||
else musicLoaded = false;
|
||||
|
||||
if (!musicLoaded)
|
||||
{
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music->ctxData);
|
||||
#else
|
||||
if (false) { }
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music->ctxData);
|
||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||
else if (music->ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music->ctxData); RL_FREE(music->ctxData); }
|
||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||
else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music->ctxData);
|
||||
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
else if (music->ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music->ctxData); RL_FREE(music->ctxData); }
|
||||
else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
|
||||
#endif
|
||||
|
||||
RL_FREE(music);
|
||||
music = NULL;
|
||||
|
||||
TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName);
|
||||
}
|
||||
|
||||
|
|
@ -1373,37 +1323,30 @@ Music LoadMusicStream(const char *fileName)
|
|||
// Unload music stream
|
||||
void UnloadMusicStream(Music music)
|
||||
{
|
||||
if (music == NULL) return;
|
||||
CloseAudioStream(music.stream);
|
||||
|
||||
CloseAudioStream(music->stream);
|
||||
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music->ctxData);
|
||||
#else
|
||||
if (false) { }
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music->ctxData);
|
||||
else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||
else if (music->ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music->ctxData); RL_FREE(music->ctxData); }
|
||||
else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); }
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||
else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music->ctxData);
|
||||
else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
else if (music->ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music->ctxData); RL_FREE(music->ctxData); }
|
||||
else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
|
||||
#endif
|
||||
|
||||
RL_FREE(music);
|
||||
}
|
||||
|
||||
// Start music playing (open stream)
|
||||
void PlayMusicStream(Music music)
|
||||
{
|
||||
if (music != NULL)
|
||||
{
|
||||
AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.buffer;
|
||||
AudioBuffer *audioBuffer = music.stream.buffer;
|
||||
|
||||
if (audioBuffer == NULL)
|
||||
{
|
||||
|
|
@ -1417,81 +1360,76 @@ void PlayMusicStream(Music music)
|
|||
// if (IsMusicPlaying(music)) PlayMusicStream(music);
|
||||
ma_uint32 frameCursorPos = audioBuffer->frameCursorPos;
|
||||
|
||||
PlayAudioStream(music->stream); // <-- This resets the cursor position.
|
||||
PlayAudioStream(music.stream); // <-- This resets the cursor position.
|
||||
|
||||
audioBuffer->frameCursorPos = frameCursorPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Pause music playing
|
||||
void PauseMusicStream(Music music)
|
||||
{
|
||||
if (music != NULL) PauseAudioStream(music->stream);
|
||||
PauseAudioStream(music.stream);
|
||||
}
|
||||
|
||||
// Resume music playing
|
||||
void ResumeMusicStream(Music music)
|
||||
{
|
||||
if (music != NULL) ResumeAudioStream(music->stream);
|
||||
ResumeAudioStream(music.stream);
|
||||
}
|
||||
|
||||
// Stop music playing (close stream)
|
||||
void StopMusicStream(Music music)
|
||||
{
|
||||
if (music == NULL) return;
|
||||
|
||||
StopAudioStream(music->stream);
|
||||
StopAudioStream(music.stream);
|
||||
|
||||
// Restart music context
|
||||
switch (music->ctxType)
|
||||
switch (music.ctxType)
|
||||
{
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music->ctxData); break;
|
||||
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_FLAC)
|
||||
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MP3)
|
||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music->ctxData, 0); break;
|
||||
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_XM)
|
||||
case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music->ctxData); break;
|
||||
case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music->ctxData); break;
|
||||
case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
|
||||
music->sampleLeft = music->sampleCount;
|
||||
music.sampleLeft = music.sampleCount;
|
||||
}
|
||||
|
||||
// Update (re-fill) music buffers if data already processed
|
||||
void UpdateMusicStream(Music music)
|
||||
{
|
||||
if (music == NULL) return;
|
||||
|
||||
bool streamEnding = false;
|
||||
|
||||
unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.buffer)->bufferSizeInFrames/2;
|
||||
unsigned int subBufferSizeInFrames = music.stream.buffer->bufferSizeInFrames/2;
|
||||
|
||||
// NOTE: Using dynamic allocation because it could require more than 16KB
|
||||
void *pcm = RL_CALLOC(subBufferSizeInFrames*music->stream.channels*music->stream.sampleSize/8, 1);
|
||||
void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1);
|
||||
|
||||
int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts
|
||||
|
||||
while (IsAudioBufferProcessed(music->stream))
|
||||
while (IsAudioBufferProcessed(music.stream))
|
||||
{
|
||||
if ((music->sampleLeft/music->stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music->stream.channels;
|
||||
else samplesCount = music->sampleLeft;
|
||||
if ((music.sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels;
|
||||
else samplesCount = music.sampleLeft;
|
||||
|
||||
switch (music->ctxType)
|
||||
switch (music.ctxType)
|
||||
{
|
||||
#if defined(SUPPORT_FILEFORMAT_OGG)
|
||||
case MUSIC_AUDIO_OGG:
|
||||
{
|
||||
// NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
|
||||
stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music->ctxData, music->stream.channels, (short *)pcm, samplesCount);
|
||||
stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)pcm, samplesCount);
|
||||
|
||||
} break;
|
||||
#endif
|
||||
|
|
@ -1499,7 +1437,7 @@ void UpdateMusicStream(Music music)
|
|||
case MUSIC_AUDIO_FLAC:
|
||||
{
|
||||
// NOTE: Returns the number of samples to process (not required)
|
||||
drflac_read_s16((drflac *)music->ctxData, samplesCount, (short *)pcm);
|
||||
drflac_read_s16((drflac *)music.ctxData, samplesCount, (short *)pcm);
|
||||
|
||||
} break;
|
||||
#endif
|
||||
|
|
@ -1507,7 +1445,7 @@ void UpdateMusicStream(Music music)
|
|||
case MUSIC_AUDIO_MP3:
|
||||
{
|
||||
// NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed
|
||||
drmp3_read_pcm_frames_f32((drmp3 *)music->ctxData, samplesCount/music->stream.channels, (float *)pcm);
|
||||
drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, samplesCount/music.stream.channels, (float *)pcm);
|
||||
|
||||
} break;
|
||||
#endif
|
||||
|
|
@ -1515,29 +1453,29 @@ void UpdateMusicStream(Music music)
|
|||
case MUSIC_MODULE_XM:
|
||||
{
|
||||
// NOTE: Internally this function considers 2 channels generation, so samplesCount/2
|
||||
jar_xm_generate_samples_16bit((jar_xm_context_t *)music->ctxData, (short *)pcm, samplesCount/2);
|
||||
jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)pcm, samplesCount/2);
|
||||
} break;
|
||||
#endif
|
||||
#if defined(SUPPORT_FILEFORMAT_MOD)
|
||||
case MUSIC_MODULE_MOD:
|
||||
{
|
||||
// NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2
|
||||
jar_mod_fillbuffer((jar_mod_context_t *)music->ctxData, (short *)pcm, samplesCount/2, 0);
|
||||
jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)pcm, samplesCount/2, 0);
|
||||
} break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
|
||||
UpdateAudioStream(music->stream, pcm, samplesCount);
|
||||
UpdateAudioStream(music.stream, pcm, samplesCount);
|
||||
|
||||
if ((music->ctxType == MUSIC_MODULE_XM) || (music->ctxType == MUSIC_MODULE_MOD))
|
||||
if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD))
|
||||
{
|
||||
if (samplesCount > 1) music->sampleLeft -= samplesCount/2;
|
||||
else music->sampleLeft -= samplesCount;
|
||||
if (samplesCount > 1) music.sampleLeft -= samplesCount/2;
|
||||
else music.sampleLeft -= samplesCount;
|
||||
}
|
||||
else music->sampleLeft -= samplesCount;
|
||||
else music.sampleLeft -= samplesCount;
|
||||
|
||||
if (music->sampleLeft <= 0)
|
||||
if (music.sampleLeft <= 0)
|
||||
{
|
||||
streamEnding = true;
|
||||
break;
|
||||
|
|
@ -1553,14 +1491,14 @@ void UpdateMusicStream(Music music)
|
|||
StopMusicStream(music); // Stop music (and reset)
|
||||
|
||||
// Decrease loopCount to stop when required
|
||||
if (music->loopCount > 1)
|
||||
if (music.loopCount > 1)
|
||||
{
|
||||
music->loopCount--; // Decrease loop count
|
||||
music.loopCount--; // Decrease loop count
|
||||
PlayMusicStream(music); // Play again
|
||||
}
|
||||
else
|
||||
{
|
||||
if (music->loopCount == 0) PlayMusicStream(music);
|
||||
if (music.loopCount == 0) PlayMusicStream(music);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1574,27 +1512,26 @@ void UpdateMusicStream(Music music)
|
|||
// Check if any music is playing
|
||||
bool IsMusicPlaying(Music music)
|
||||
{
|
||||
if (music == NULL) return false;
|
||||
else return IsAudioStreamPlaying(music->stream);
|
||||
return IsAudioStreamPlaying(music.stream);
|
||||
}
|
||||
|
||||
// Set volume for music
|
||||
void SetMusicVolume(Music music, float volume)
|
||||
{
|
||||
if (music != NULL) SetAudioStreamVolume(music->stream, volume);
|
||||
SetAudioStreamVolume(music.stream, volume);
|
||||
}
|
||||
|
||||
// Set pitch for music
|
||||
void SetMusicPitch(Music music, float pitch)
|
||||
{
|
||||
if (music != NULL) SetAudioStreamPitch(music->stream, pitch);
|
||||
SetAudioStreamPitch(music.stream, pitch);
|
||||
}
|
||||
|
||||
// Set music loop count (loop repeats)
|
||||
// NOTE: If set to -1, means infinite loop
|
||||
void SetMusicLoopCount(Music music, int count)
|
||||
{
|
||||
if (music != NULL) music->loopCount = count;
|
||||
music.loopCount = count;
|
||||
}
|
||||
|
||||
// Get music time length (in seconds)
|
||||
|
|
@ -1602,7 +1539,7 @@ float GetMusicTimeLength(Music music)
|
|||
{
|
||||
float totalSeconds = 0.0f;
|
||||
|
||||
if (music != NULL) totalSeconds = (float)music->sampleCount/(music->stream.sampleRate*music->stream.channels);
|
||||
totalSeconds = (float)music.sampleCount/(music.stream.sampleRate*music.stream.channels);
|
||||
|
||||
return totalSeconds;
|
||||
}
|
||||
|
|
@ -1612,11 +1549,8 @@ float GetMusicTimePlayed(Music music)
|
|||
{
|
||||
float secondsPlayed = 0.0f;
|
||||
|
||||
if (music != NULL)
|
||||
{
|
||||
unsigned int samplesPlayed = music->sampleCount - music->sampleLeft;
|
||||
secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels);
|
||||
}
|
||||
unsigned int samplesPlayed = music.sampleCount - music.sampleLeft;
|
||||
secondsPlayed = (float)samplesPlayed/(music.stream.sampleRate*music.stream.channels);
|
||||
|
||||
return secondsPlayed;
|
||||
}
|
||||
|
|
|
|||
62
src/raudio.h
62
src/raudio.h
|
|
@ -80,25 +80,14 @@
|
|||
|
||||
// Wave type, defines audio wave data
|
||||
typedef struct Wave {
|
||||
unsigned int sampleCount; // Number of samples
|
||||
unsigned int sampleCount; // Total number of samples
|
||||
unsigned int sampleRate; // Frequency (samples per second)
|
||||
unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
|
||||
unsigned int channels; // Number of channels (1-mono, 2-stereo)
|
||||
void *data; // Buffer data pointer
|
||||
} Wave;
|
||||
|
||||
// Sound source type
|
||||
typedef struct Sound {
|
||||
void *audioBuffer; // Pointer to internal data used by the audio system
|
||||
|
||||
unsigned int source; // Audio source id
|
||||
unsigned int buffer; // Audio buffer id
|
||||
int format; // Audio format specifier
|
||||
} Sound;
|
||||
|
||||
// Music type (file streaming from memory)
|
||||
// NOTE: Anything longer than ~10 seconds should be streamed
|
||||
typedef struct MusicData *Music;
|
||||
typedef struct rAudioBuffer rAudioBuffer;
|
||||
|
||||
// Audio stream type
|
||||
// NOTE: Useful to create custom audio streams not bound to a specific file
|
||||
|
|
@ -107,13 +96,28 @@ typedef struct AudioStream {
|
|||
unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
|
||||
unsigned int channels; // Number of channels (1-mono, 2-stereo)
|
||||
|
||||
void *audioBuffer; // Pointer to internal data used by the audio system.
|
||||
|
||||
int format; // Audio format specifier
|
||||
unsigned int source; // Audio source id
|
||||
unsigned int buffers[2]; // Audio buffers (double buffering)
|
||||
rAudioBuffer *buffer; // Pointer to internal data used by the audio system
|
||||
} AudioStream;
|
||||
|
||||
// Sound source type
|
||||
typedef struct Sound {
|
||||
unsigned int sampleCount; // Total number of samples
|
||||
AudioStream stream; // Audio stream
|
||||
} Sound;
|
||||
|
||||
// Music stream type (audio file streaming from memory)
|
||||
// NOTE: Anything longer than ~10 seconds should be streamed
|
||||
typedef struct Music {
|
||||
int ctxType; // Type of music context (audio filetype)
|
||||
void *ctxData; // Audio context data, depends on type
|
||||
|
||||
unsigned int sampleCount; // Total number of samples
|
||||
unsigned int sampleLeft; // Number of samples left to end
|
||||
unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop
|
||||
|
||||
AudioStream stream; // Audio stream
|
||||
} Music;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // Prevents name mangling of functions
|
||||
#endif
|
||||
|
|
@ -126,25 +130,31 @@ extern "C" { // Prevents name mangling of functions
|
|||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Declaration
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Audio device management functions
|
||||
void InitAudioDevice(void); // Initialize audio device and context
|
||||
void CloseAudioDevice(void); // Close the audio device and context
|
||||
bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
|
||||
void SetMasterVolume(float volume); // Set master volume (listener)
|
||||
|
||||
// Wave/Sound loading/unloading functions
|
||||
Wave LoadWave(const char *fileName); // Load wave data from file
|
||||
Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data
|
||||
Sound LoadSound(const char *fileName); // Load sound from file
|
||||
Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
|
||||
void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
|
||||
void UnloadWave(Wave wave); // Unload wave data
|
||||
void UnloadSound(Sound sound); // Unload sound
|
||||
void ExportWave(Wave wave, const char *fileName); // Export wave data to file
|
||||
void ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h)
|
||||
|
||||
// Wave/Sound management functions
|
||||
void PlaySound(Sound sound); // Play a sound
|
||||
void PlaySoundMulti(Sound sound); // Play a sound using the multi channel buffer pool
|
||||
int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel buffer pool
|
||||
void StopSound(Sound sound); // Stop playing a sound
|
||||
void PauseSound(Sound sound); // Pause a sound
|
||||
void ResumeSound(Sound sound); // Resume a paused sound
|
||||
void StopSound(Sound sound); // Stop playing a sound
|
||||
void StopSoundMulti(void); // Stop any sound played with PlaySoundMulti()
|
||||
void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool)
|
||||
void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool)
|
||||
int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel
|
||||
bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
|
||||
void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
|
||||
void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
|
||||
|
|
@ -152,6 +162,8 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // C
|
|||
Wave WaveCopy(Wave wave); // Copy a wave to a new wave
|
||||
void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range
|
||||
float *GetWaveData(Wave wave); // Get samples data from wave as a floats array
|
||||
|
||||
// Music management functions
|
||||
Music LoadMusicStream(const char *fileName); // Load music stream from file
|
||||
void UnloadMusicStream(Music music); // Unload music stream
|
||||
void PlayMusicStream(Music music); // Start music playing
|
||||
|
|
@ -167,9 +179,7 @@ float GetMusicTimeLength(Music music); // Get music tim
|
|||
float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
|
||||
|
||||
// AudioStream management functions
|
||||
AudioStream InitAudioStream(unsigned int sampleRate,
|
||||
unsigned int sampleSize,
|
||||
unsigned int channels); // Init audio stream (to stream raw audio pcm data)
|
||||
AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data)
|
||||
void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data
|
||||
void CloseAudioStream(AudioStream stream); // Close audio stream and free memory
|
||||
bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
|
||||
|
|
|
|||
69
src/raylib.h
69
src/raylib.h
|
|
@ -114,40 +114,40 @@
|
|||
// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
|
||||
// Plain structures in C++ (without constructors) can be initialized from { } initializers.
|
||||
#if defined(__cplusplus)
|
||||
#define CLITERAL
|
||||
#define CLITERAL(type) type
|
||||
#else
|
||||
#define CLITERAL (Color)
|
||||
#define CLITERAL(type) (type)
|
||||
#endif
|
||||
|
||||
// Some Basic Colors
|
||||
// NOTE: Custom raylib color palette for amazing visuals on WHITE background
|
||||
#define LIGHTGRAY CLITERAL{ 200, 200, 200, 255 } // Light Gray
|
||||
#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray
|
||||
#define DARKGRAY CLITERAL{ 80, 80, 80, 255 } // Dark Gray
|
||||
#define YELLOW CLITERAL{ 253, 249, 0, 255 } // Yellow
|
||||
#define GOLD CLITERAL{ 255, 203, 0, 255 } // Gold
|
||||
#define ORANGE CLITERAL{ 255, 161, 0, 255 } // Orange
|
||||
#define PINK CLITERAL{ 255, 109, 194, 255 } // Pink
|
||||
#define RED CLITERAL{ 230, 41, 55, 255 } // Red
|
||||
#define MAROON CLITERAL{ 190, 33, 55, 255 } // Maroon
|
||||
#define GREEN CLITERAL{ 0, 228, 48, 255 } // Green
|
||||
#define LIME CLITERAL{ 0, 158, 47, 255 } // Lime
|
||||
#define DARKGREEN CLITERAL{ 0, 117, 44, 255 } // Dark Green
|
||||
#define SKYBLUE CLITERAL{ 102, 191, 255, 255 } // Sky Blue
|
||||
#define BLUE CLITERAL{ 0, 121, 241, 255 } // Blue
|
||||
#define DARKBLUE CLITERAL{ 0, 82, 172, 255 } // Dark Blue
|
||||
#define PURPLE CLITERAL{ 200, 122, 255, 255 } // Purple
|
||||
#define VIOLET CLITERAL{ 135, 60, 190, 255 } // Violet
|
||||
#define DARKPURPLE CLITERAL{ 112, 31, 126, 255 } // Dark Purple
|
||||
#define BEIGE CLITERAL{ 211, 176, 131, 255 } // Beige
|
||||
#define BROWN CLITERAL{ 127, 106, 79, 255 } // Brown
|
||||
#define DARKBROWN CLITERAL{ 76, 63, 47, 255 } // Dark Brown
|
||||
#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
|
||||
#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray
|
||||
#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray
|
||||
#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow
|
||||
#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold
|
||||
#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange
|
||||
#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink
|
||||
#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red
|
||||
#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon
|
||||
#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green
|
||||
#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime
|
||||
#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green
|
||||
#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue
|
||||
#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue
|
||||
#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue
|
||||
#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple
|
||||
#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet
|
||||
#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple
|
||||
#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige
|
||||
#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown
|
||||
#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown
|
||||
|
||||
#define WHITE CLITERAL{ 255, 255, 255, 255 } // White
|
||||
#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black
|
||||
#define BLANK CLITERAL{ 0, 0, 0, 0 } // Blank (Transparent)
|
||||
#define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta
|
||||
#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo)
|
||||
#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White
|
||||
#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black
|
||||
#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent)
|
||||
#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta
|
||||
#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo)
|
||||
|
||||
// Temporal hack to avoid breaking old codebases using
|
||||
// deprecated raylib implementation of these functions
|
||||
|
|
@ -433,7 +433,7 @@ typedef struct Sound {
|
|||
|
||||
// Music stream type (audio file streaming from memory)
|
||||
// NOTE: Anything longer than ~10 seconds should be streamed
|
||||
typedef struct MusicStream {
|
||||
typedef struct Music {
|
||||
int ctxType; // Type of music context (audio filetype)
|
||||
void *ctxData; // Audio context data, depends on type
|
||||
|
||||
|
|
@ -442,7 +442,7 @@ typedef struct MusicStream {
|
|||
unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop
|
||||
|
||||
AudioStream stream; // Audio stream
|
||||
} MusicStream, *Music;
|
||||
} Music;
|
||||
|
||||
// Head-Mounted-Display device parameters
|
||||
typedef struct VrDeviceInfo {
|
||||
|
|
@ -1350,7 +1350,6 @@ RLAPI void SetMasterVolume(float volume); // Set mas
|
|||
|
||||
// Wave/Sound loading/unloading functions
|
||||
RLAPI Wave LoadWave(const char *fileName); // Load wave data from file
|
||||
RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data
|
||||
RLAPI Sound LoadSound(const char *fileName); // Load sound from file
|
||||
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
|
||||
RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
|
||||
|
|
@ -1361,12 +1360,12 @@ RLAPI void ExportWaveAsCode(Wave wave, const char *fileName); // Export
|
|||
|
||||
// Wave/Sound management functions
|
||||
RLAPI void PlaySound(Sound sound); // Play a sound
|
||||
RLAPI void PlaySoundMulti(Sound sound); // Play a sound using the multi channel buffer pool
|
||||
RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel buffer pool
|
||||
RLAPI void StopSound(Sound sound); // Stop playing a sound
|
||||
RLAPI void PauseSound(Sound sound); // Pause a sound
|
||||
RLAPI void ResumeSound(Sound sound); // Resume a paused sound
|
||||
RLAPI void StopSound(Sound sound); // Stop playing a sound
|
||||
RLAPI void StopSoundMulti(void); // Stop any sound played with PlaySoundMulti()
|
||||
RLAPI void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool)
|
||||
RLAPI void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool)
|
||||
RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel
|
||||
RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
|
||||
RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
|
||||
RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
|
||||
|
|
|
|||
24
src/rlgl.h
24
src/rlgl.h
|
|
@ -735,7 +735,7 @@ typedef struct DrawCall {
|
|||
int mode; // Drawing mode: LINES, TRIANGLES, QUADS
|
||||
int vertexCount; // Number of vertex of the draw
|
||||
int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES)
|
||||
//unsigned int vaoId; // Vertex Array id to be used on the draw
|
||||
//unsigned int vaoId; // Vertex array id to be used on the draw
|
||||
//unsigned int shaderId; // Shader id to be used on the draw
|
||||
unsigned int textureId; // Texture id to be used on the draw
|
||||
// TODO: Support additional texture units?
|
||||
|
|
@ -1140,8 +1140,8 @@ void rlEnd(void)
|
|||
{
|
||||
// WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(),
|
||||
// we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call!
|
||||
// Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw
|
||||
rlPopMatrix();
|
||||
// If we have multiple matrix pushed, it will require "stackCounter" pops before launching the draw
|
||||
for (int i = stackCounter; i >= 0; i--) rlPopMatrix();
|
||||
rlglDraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1291,11 +1291,11 @@ void rlTextureParameters(unsigned int id, int param, int value)
|
|||
{
|
||||
if (value == RL_WRAP_MIRROR_CLAMP)
|
||||
{
|
||||
#if !defined(GRAPHICS_API_OPENGL_11)
|
||||
if (!texMirrorClampSupported) TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported");
|
||||
#endif
|
||||
if (texMirrorClampSupported) glTexParameteri(GL_TEXTURE_2D, param, value);
|
||||
else TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported");
|
||||
}
|
||||
else glTexParameteri(GL_TEXTURE_2D, param, value);
|
||||
|
||||
} break;
|
||||
case RL_TEXTURE_MAG_FILTER:
|
||||
case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break;
|
||||
|
|
@ -2623,8 +2623,16 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform)
|
|||
Matrix matView = modelview; // View matrix (camera)
|
||||
Matrix matProjection = projection; // Projection matrix (perspective)
|
||||
|
||||
// Calculate model-view matrix combining matModel and matView
|
||||
Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates
|
||||
// TODO: Matrix nightmare! Trying to combine stack matrices with view matrix and local model transform matrix..
|
||||
// There is some problem in the order matrices are multiplied... it requires some time to figure out...
|
||||
Matrix matStackTransform = MatrixIdentity();
|
||||
|
||||
// TODO: Consider possible transform matrices in the stack
|
||||
// Is this the right order? or should we start with the first stored matrix instead of the last one?
|
||||
//for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform);
|
||||
|
||||
Matrix matModel = MatrixMultiply(transform, matStackTransform); // Apply local model transformation
|
||||
Matrix matModelView = MatrixMultiply(matModel, matView); // Transform to camera-space coordinates
|
||||
//-----------------------------------------------------
|
||||
|
||||
// Bind active texture maps (if available)
|
||||
|
|
|
|||
128
src/rmem.h
128
src/rmem.h
|
|
@ -77,9 +77,13 @@ typedef struct Stack {
|
|||
size_t size;
|
||||
} Stack;
|
||||
|
||||
#define MEMPOOL_BUCKET_SIZE 8
|
||||
#define MEMPOOL_BUCKET_BITS 3
|
||||
|
||||
typedef struct MemPool {
|
||||
AllocList freeList;
|
||||
Stack stack;
|
||||
MemNode *buckets[MEMPOOL_BUCKET_SIZE];
|
||||
} MemPool;
|
||||
|
||||
// Object Pool
|
||||
|
|
@ -164,10 +168,19 @@ static inline size_t __AlignSize(const size_t size, const size_t align)
|
|||
return (size + (align - 1)) & -align;
|
||||
}
|
||||
|
||||
static void __RemoveNode(MemNode **const node)
|
||||
static void __RemoveNode(MemPool *const mempool, MemNode **const node)
|
||||
{
|
||||
((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (*node = (*node)->next);
|
||||
((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (*node = (*node)->prev);
|
||||
if ((*node)->next != NULL) (*node)->next->prev = (*node)->prev;
|
||||
else {
|
||||
mempool->freeList.tail = (*node)->prev;
|
||||
if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL;
|
||||
}
|
||||
|
||||
if ((*node)->prev != NULL) (*node)->prev->next = (*node)->next;
|
||||
else {
|
||||
mempool->freeList.head = (*node)->next;
|
||||
if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
@ -183,7 +196,7 @@ MemPool CreateMemPool(const size_t size)
|
|||
{
|
||||
// Align the mempool size to at least the size of an alloc node.
|
||||
mempool.stack.size = size;
|
||||
mempool.stack.mem = malloc(1 + mempool.stack.size*sizeof *mempool.stack.mem);
|
||||
mempool.stack.mem = malloc(mempool.stack.size*sizeof *mempool.stack.mem);
|
||||
|
||||
if (mempool.stack.mem==NULL)
|
||||
{
|
||||
|
|
@ -229,8 +242,16 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
|
|||
{
|
||||
MemNode *new_mem = NULL;
|
||||
const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t));
|
||||
const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1;
|
||||
|
||||
if (mempool->freeList.head != NULL)
|
||||
if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE)
|
||||
{
|
||||
new_mem = mempool->buckets[BUCKET_INDEX];
|
||||
mempool->buckets[BUCKET_INDEX] = mempool->buckets[BUCKET_INDEX]->next;
|
||||
if( mempool->buckets[BUCKET_INDEX] != NULL )
|
||||
mempool->buckets[BUCKET_INDEX]->prev = NULL;
|
||||
}
|
||||
else if (mempool->freeList.head != NULL)
|
||||
{
|
||||
const size_t MEM_SPLIT_THRESHOLD = 16;
|
||||
|
||||
|
|
@ -242,9 +263,8 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
|
|||
{
|
||||
// Close in size - reduce fragmentation by not splitting.
|
||||
new_mem = *inode;
|
||||
__RemoveNode(inode);
|
||||
__RemoveNode(mempool, inode);
|
||||
mempool->freeList.len--;
|
||||
new_mem->next = new_mem->prev = NULL;
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
|
@ -253,7 +273,6 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
|
|||
new_mem = (MemNode *)((uint8_t *)*inode + ((*inode)->size - ALLOC_SIZE));
|
||||
(*inode)->size -= ALLOC_SIZE;
|
||||
new_mem->size = ALLOC_SIZE;
|
||||
new_mem->next = new_mem->prev = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -272,19 +291,20 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size)
|
|||
// Use the available mempool space as the new node.
|
||||
new_mem = (MemNode *)mempool->stack.base;
|
||||
new_mem->size = ALLOC_SIZE;
|
||||
new_mem->next = new_mem->prev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Visual of the allocation block.
|
||||
// --------------
|
||||
// | mem size | lowest addr of block
|
||||
// | next node |
|
||||
// | next node | 12 byte (32-bit) header
|
||||
// | prev node | 24 byte (64-bit) header
|
||||
// --------------
|
||||
// | alloc'd |
|
||||
// | memory |
|
||||
// | space | highest addr of block
|
||||
// --------------
|
||||
new_mem->next = new_mem->prev = NULL;
|
||||
uint8_t *const final_mem = (uint8_t *)new_mem + sizeof *new_mem;
|
||||
memset(final_mem, 0, new_mem->size - sizeof *new_mem);
|
||||
return final_mem;
|
||||
|
|
@ -296,17 +316,17 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si
|
|||
if ((mempool == NULL) || (size > mempool->stack.size)) return NULL;
|
||||
// NULL ptr should make this work like regular Allocation.
|
||||
else if (ptr == NULL) return MemPoolAlloc(mempool, size);
|
||||
else if ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem) return NULL;
|
||||
else if ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem) return NULL;
|
||||
else
|
||||
{
|
||||
MemNode *node = (MemNode *)((uint8_t *)ptr - sizeof *node);
|
||||
MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node);
|
||||
const size_t NODE_SIZE = sizeof *node;
|
||||
uint8_t *resized_block = MemPoolAlloc(mempool, size);
|
||||
uint8_t *const resized_block = MemPoolAlloc(mempool, size);
|
||||
|
||||
if (resized_block == NULL) return NULL;
|
||||
else
|
||||
{
|
||||
MemNode *resized = (MemNode *)(resized_block - sizeof *resized);
|
||||
MemNode *const resized = (MemNode *)(resized_block - sizeof *resized);
|
||||
memmove(resized_block, ptr, (node->size > resized->size)? (resized->size - NODE_SIZE) : (node->size - NODE_SIZE));
|
||||
MemPoolFree(mempool, ptr);
|
||||
return resized_block;
|
||||
|
|
@ -316,11 +336,12 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si
|
|||
|
||||
void MemPoolFree(MemPool *const restrict mempool, void *ptr)
|
||||
{
|
||||
if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem)) return;
|
||||
if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return;
|
||||
else
|
||||
{
|
||||
// Behind the actual pointer data is the allocation info.
|
||||
MemNode *mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node);
|
||||
MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node);
|
||||
const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1;
|
||||
|
||||
// Make sure the pointer data is valid.
|
||||
if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) ||
|
||||
|
|
@ -332,52 +353,44 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr)
|
|||
{
|
||||
mempool->stack.base += mem_node->size;
|
||||
}
|
||||
// attempted stack merge failed, try to place it into the memnode buckets
|
||||
else if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE)
|
||||
{
|
||||
if (mempool->buckets[index] == NULL) mempool->buckets[index] = node;
|
||||
else
|
||||
{
|
||||
for (MemNode *n = mempool->buckets[index]; n != NULL; n = n->next) if( n==node ) return;
|
||||
mempool->buckets[index]->prev = node;
|
||||
node->next = mempool->buckets[index];
|
||||
mempool->buckets[index] = node;
|
||||
}
|
||||
}
|
||||
// Otherwise, we add it to the free list.
|
||||
// We also check if the freelist already has the pointer so we can prevent double frees.
|
||||
else if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))
|
||||
else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/
|
||||
{
|
||||
for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return;
|
||||
|
||||
// This code inserts at head.
|
||||
/*
|
||||
( mempool->freeList.head==NULL)? (mempool->freeList.tail = mem_node) : (mempool->freeList.head->prev = mem_node);
|
||||
mem_node->next = mempool->freeList.head;
|
||||
mempool->freeList.head = mem_node;
|
||||
mempool->freeList.len++;
|
||||
*/
|
||||
|
||||
// This code insertion sorts where largest size is first.
|
||||
// This code insertion sorts where largest size is last.
|
||||
if (mempool->freeList.head == NULL)
|
||||
{
|
||||
mempool->freeList.head = mempool->freeList.tail = mem_node;
|
||||
mempool->freeList.len++;
|
||||
}
|
||||
else if (mempool->freeList.head->size <= mem_node->size)
|
||||
else if (mempool->freeList.head->size >= mem_node->size)
|
||||
{
|
||||
mem_node->next = mempool->freeList.head;
|
||||
mem_node->next->prev = mem_node;
|
||||
mempool->freeList.head = mem_node;
|
||||
mempool->freeList.len++;
|
||||
}
|
||||
else if (mempool->freeList.tail->size > mem_node->size)
|
||||
else //if (mempool->freeList.tail->size <= mem_node->size)
|
||||
{
|
||||
mem_node->prev = mempool->freeList.tail;
|
||||
mempool->freeList.tail->next = mem_node;
|
||||
mempool->freeList.tail = mem_node;
|
||||
mempool->freeList.len++;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemNode *n = mempool->freeList.head;
|
||||
while ((n->next != NULL) && (n->next->size > mem_node->size)) n = n->next;
|
||||
|
||||
mem_node->next = n->next;
|
||||
if (n->next != NULL) mem_node->next->prev = mem_node;
|
||||
|
||||
n->next = mem_node;
|
||||
mem_node->prev = n;
|
||||
mempool->freeList.len++;
|
||||
}
|
||||
|
||||
if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool);
|
||||
}
|
||||
|
|
@ -400,6 +413,8 @@ size_t GetMemPoolFreeMemory(const MemPool mempool)
|
|||
|
||||
for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size;
|
||||
|
||||
for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++) for (MemNode *n = mempool.buckets[i]; n != NULL; n = n->next) total_remaining += n->size;
|
||||
|
||||
return total_remaining;
|
||||
}
|
||||
|
||||
|
|
@ -411,12 +426,29 @@ bool MemPoolDefrag(MemPool *const mempool)
|
|||
// If the memory pool has been entirely released, fully defrag it.
|
||||
if (mempool->stack.size == GetMemPoolFreeMemory(*mempool))
|
||||
{
|
||||
memset(&mempool->freeList, 0, sizeof mempool->freeList);
|
||||
mempool->freeList.head = mempool->freeList.tail = NULL;
|
||||
mempool->freeList.len = 0;
|
||||
for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL;
|
||||
mempool->stack.base = mempool->stack.mem + mempool->stack.size;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++)
|
||||
{
|
||||
while (mempool->buckets[i] != NULL)
|
||||
{
|
||||
if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base)
|
||||
{
|
||||
mempool->stack.base += mempool->buckets[i]->size;
|
||||
mempool->buckets[i]->size = 0;
|
||||
mempool->buckets[i] = mempool->buckets[i]->next;
|
||||
if (mempool->buckets[i] != NULL) mempool->buckets[i]->prev = NULL;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t PRE_DEFRAG_LEN = mempool->freeList.len;
|
||||
MemNode **node = &mempool->freeList.head;
|
||||
|
||||
|
|
@ -427,7 +459,7 @@ bool MemPoolDefrag(MemPool *const mempool)
|
|||
// If node is right at the stack, merge it back into the stack.
|
||||
mempool->stack.base += (*node)->size;
|
||||
(*node)->size = 0UL;
|
||||
__RemoveNode(node);
|
||||
__RemoveNode(mempool, node);
|
||||
mempool->freeList.len--;
|
||||
node = &mempool->freeList.head;
|
||||
}
|
||||
|
|
@ -475,6 +507,7 @@ bool MemPoolDefrag(MemPool *const mempool)
|
|||
(*node)->size = 0UL;
|
||||
(*node)->next->prev = (*node)->prev;
|
||||
(*node)->prev->next = (*node)->next;
|
||||
*node = (*node)->next;
|
||||
|
||||
mempool->freeList.len--;
|
||||
node = &mempool->freeList.head;
|
||||
|
|
@ -487,6 +520,7 @@ bool MemPoolDefrag(MemPool *const mempool)
|
|||
(*node)->size = 0UL;
|
||||
(*node)->next->prev = (*node)->prev;
|
||||
(*node)->prev->next = (*node)->next;
|
||||
*node = (*node)->prev;
|
||||
|
||||
mempool->freeList.len--;
|
||||
node = &mempool->freeList.head;
|
||||
|
|
@ -513,7 +547,7 @@ void ToggleMemPoolAutoDefrag(MemPool *const mempool)
|
|||
//----------------------------------------------------------------------------------
|
||||
union ObjInfo {
|
||||
uint8_t *const byte;
|
||||
size_t *const size;
|
||||
size_t *const index;
|
||||
};
|
||||
|
||||
ObjPool CreateObjPool(const size_t objsize, const size_t len)
|
||||
|
|
@ -537,7 +571,7 @@ ObjPool CreateObjPool(const size_t objsize, const size_t len)
|
|||
for (size_t i=0; i<objpool.freeBlocks; i++)
|
||||
{
|
||||
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
|
||||
*block.size = i + 1;
|
||||
*block.index = i + 1;
|
||||
}
|
||||
|
||||
objpool.stack.base = objpool.stack.mem;
|
||||
|
|
@ -561,7 +595,7 @@ ObjPool CreateObjPoolFromBuffer(void *const buf, const size_t objsize, const siz
|
|||
for (size_t i=0; i<objpool.freeBlocks; i++)
|
||||
{
|
||||
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
|
||||
*block.size = i + 1;
|
||||
*block.index = i + 1;
|
||||
}
|
||||
|
||||
objpool.stack.base = objpool.stack.mem;
|
||||
|
|
@ -594,7 +628,7 @@ void *ObjPoolAlloc(ObjPool *const objpool)
|
|||
|
||||
// after allocating, we set head to the address of the index that *Head holds.
|
||||
// Head = &pool[*Head * pool.objsize];
|
||||
objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.size*objpool->objSize) : NULL;
|
||||
objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL;
|
||||
memset(ret.byte, 0, objpool->objSize);
|
||||
return ret.byte;
|
||||
}
|
||||
|
|
@ -611,7 +645,7 @@ void ObjPoolFree(ObjPool *const restrict objpool, void *ptr)
|
|||
// When we free our pointer, we recycle the pointer space to store the previous index and then we push it as our new head.
|
||||
// *p = index of Head in relation to the buffer;
|
||||
// Head = p;
|
||||
*p.size = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size;
|
||||
*p.index = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size;
|
||||
objpool->stack.base = p.byte;
|
||||
objpool->freeBlocks++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
|
|||
endif
|
||||
|
||||
# Define a custom shell .html and output extension
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html
|
||||
CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html
|
||||
EXT = .html
|
||||
endif
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user