soundpong

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/soundpong
Log | Files | Refs

commit ccd894260a6bf23db4ebb6e1983092b14d549b25
parent 9e7f2cba127d91dea104e945658bdca6c0fd52ed
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sat,  2 Oct 2021 11:58:50 -0500

fluidsynth and antialiased balls

Diffstat:
AMakefile | 13+++++++++++++
Mmain.c | 274+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
2 files changed, 171 insertions(+), 116 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,13 @@ +SRC = main.c SDL2_gfxPrimitives.c SDL2_rotozoom.c +OBJ = $(SRC:%.c=%.o) +EXE = pong +LIBS = -lSDL2 -lm -lfluidsynth -lSDL2_gfx + +all: $(OBJ) + $(CC) -g -o $(EXE) $(OBJ) $(LIBS) + +.c.o: + $(CC) -g $< -c + +clean: + rm -rf $(OBJS) $(EXE) diff --git a/main.c b/main.c @@ -1,12 +1,22 @@ +#include <fluidsynth.h> #include <SDL2/SDL.h> #include <stdbool.h> +#include "SDL2_gfxPrimitives.h" #include <stdio.h> -#define BALL_RADIUS 30 +#define BALL_RADIUS 11 #define DROPRATE 2000 -#define MINLENGTH 10 +#define MINLENGTH 20 -const float rate = .05; +#ifdef SDL_Log +#undef SDL_log +#define SDL_Log do {} while(0); +#endif + +#define LOWEST 45 +#define HIGHEST 100 + +const float rate = .01; const float G = 1; struct SDL_Rect screenrect = { 0, 0, 500, 1000 }; @@ -15,9 +25,11 @@ struct SDL_MouseMotionEvent mousestate; SDL_Renderer *ren; bool dropball; +fluid_synth_t *fsynth; + struct { int x, y; -} dropper = { .x = 50, .y = 50 }; +} dropper = { .x = 100, .y = 100 }; struct ball { float x, y; @@ -42,73 +54,82 @@ struct line *lines_last; bool ismousedown; SDL_Point mousedown; -// This is not efficient -void -draw_circle(int x, int y, int radius) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define BETWEEN(x, a, b) (((x) <= MAX((a), (b))) && (x >= MIN((a), (b)))) +#define INRADIUS(a, b, c) ((a)*(a) + (b)*(b) <= (c)*(c)) + +SDL_Point +ball_intersects_line(struct ball *ball, struct line *line) { - SDL_Point start; - SDL_Point end; - Uint8 r, g, b, a; - SDL_GetRenderDrawColor(ren, &r, &g, &b, &a); - - bool started = false; - double rx, frac; - int ix; - for (int i = -radius; i <= radius; i++) { - start.y = end.y = i; - rx = sqrt(radius*radius - i*i); - ix = sqrt(radius*radius - i*i); - start.x = -sqrt(radius*radius - i*i); - end.x = sqrt(radius*radius - i*i); - frac = rx - ix; - - SDL_SetRenderDrawColor(ren, r, g, b, a); - SDL_RenderDrawLine(ren, x + start.x, y + start.y, x + end.x, y + end.y); - - // antialiasing - SDL_SetRenderDrawColor(ren, r, g, b, a*frac); - SDL_RenderDrawPoint(ren, x + start.x-1, y + start.y); - SDL_RenderDrawPoint(ren, x + start.y, y + start.x-1); - SDL_RenderDrawPoint(ren, x + end.x+1, y + end.y); - SDL_RenderDrawPoint(ren, x + end.y, y + end.x+1); - started = false; + /* we calculate the distance from the center of the ball to the line*/ + float a = -(line->end.y - line->start.y); + float b = line->end.x - line->start.x; + float c = line->start.x*(line->end.y - line->start.y) - line->start.y*(line->end.x - line->start.x); + float x = (b*(b*ball->x - a*ball->y) - a*c) / (a*a + b*b); + float y = (a*(-b*ball->x + a*ball->y) - b*c) / (a*a + b*b); + float dist = fabsf(a*ball->x + b*ball->y + c) / sqrtf(a*a+b*b); + + /* if the distance between the center of the circle and the intersection is less + * than the radius, then the line and circle intersect AND the intersection is on + * the finite segment */ + if (BETWEEN(x, line->start.x, line->end.x) && BETWEEN(y, line->start.y, line->end.y)) { + if (dist <= BALL_RADIUS) + return (SDL_Point){x, y}; + } else { // otherwise check if the endpoints intersect + if (INRADIUS(ball->x - line->start.x, ball->y - line->start.y, BALL_RADIUS)) + return (SDL_Point){line->start.x, line->start.y}; + if (INRADIUS(ball->x - line->end.x, ball->y - line->end.y, BALL_RADIUS)) + return (SDL_Point){line->end.x, line->end.y}; } + + return (SDL_Point){-1, -1}; } -#define PF_RGBA32 SDL_PIXELFORMAT_RGBA8888 -const SDL_Point zero = {0, 0}; +#define DEG(x) (180*((x)/M_PI)) void -draw_line(struct line line) +play_vec(float vx, float vy) { - int w = abs(line.start.x - line.end.x); - int h = abs(line.start.y - line.end.y); - int len = sqrt(w*w +h*h); - double angle = 180*atan(((double) h)/w)/M_PI; - - if (line.start.y > line.end.y) - angle = -angle; - - if (line.start.x > line.end.x) - angle = 180 - angle; - - SDL_Rect src = {0, 0, len, 5}; - SDL_Rect dest = {line.start.x, line.start.y, len, 2}; - SDL_Texture *tex = SDL_CreateTexture(ren, PF_RGBA32, SDL_TEXTUREACCESS_TARGET, src.w, src.h); - if (tex == NULL) { - SDL_Log("%s warning: failed to create texture", __func__); - return; - } + int val = sqrt(vx*vx + vy*vy) / 2 + LOWEST; + if (fluid_synth_noteon(fsynth, 0, val, 50) == FLUID_FAILED) + fluid_synth_noteoff(fsynth, 0, val); +} - SDL_SetRenderTarget(ren, tex); - SDL_SetRenderDrawColor(ren, 255, 255, 255, 255); - SDL_RenderClear(ren); +bool +ball_bounce(struct ball *ball, struct line *line) +{ + float vx = ball->vx, vy = ball->vy; + SDL_Point i = ball_intersects_line(ball, line); + if (i.x < 0 && i.y < 0) + return false; + + play_vec(vx, vy); + + // if we intersect and endpoint, just bounce in the opposite direction + if ((i.x == line->start.x && i.y == line->start.y) || (i.x == line->end.x && i.y == line->end.y)) { + ball->vx = -ball->vx; + ball->vy = -ball->vy; + return true; + } - SDL_SetRenderTarget(ren, NULL); - SDL_RenderCopyEx(ren, tex, NULL, &dest, angle, &zero, SDL_FLIP_NONE); - SDL_DestroyTexture(tex); + float dy = line->end.y - line->start.y; + float dx = line->end.x - line->start.x; + float nx = dy/sqrt(dx*dx + dy*dy), ny = -dx/sqrt(dx*dx + dy*dy); + float coef = (vx * nx + vy * ny) / (nx*nx + ny*ny); + float ux = coef * nx; + float uy = coef * ny; + float wx = vx - ux; + float wy = vy - uy; + ball->vx = wx - ux; + ball->vy = wy - uy; + return true; } +#define PF_RGBA32 SDL_PIXELFORMAT_RGBA8888 + +const SDL_Point zero = {0, 0}; + void * xcalloc(int size) { @@ -177,17 +198,28 @@ line_del(struct line *line) void ball_del(struct ball *ball) { + if (ball == NULL) + return; + if (ball->next == NULL && ball->prev == NULL) { + balls_first = balls_last = NULL; + free(ball); + return; + } + if (ball == balls_first) { balls_first = balls_first->next; + balls_first->prev = NULL; } else { ball->prev->next = ball->next; } if (ball == balls_last) { balls_last = balls_last->prev; + balls_last->next = NULL; } else { ball->next->prev = ball->prev; } + free(ball); } @@ -209,45 +241,6 @@ setdropball(unsigned int interval, void *param) return interval; } - -const struct SDL_Rect dropper_srcrect = { 0, 0, 2*BALL_RADIUS + 10 + 2, 2*BALL_RADIUS + 10 + 3}; -SDL_Texture * -render_dropper() -{ - int pitch = SDL_BYTESPERPIXEL(PF_RGBA32)*dropper_srcrect.w; - SDL_Texture *tex = SDL_CreateTexture(ren, PF_RGBA32, SDL_TEXTUREACCESS_STATIC, dropper_srcrect.w, dropper_srcrect.h); - if (tex == NULL) { - SDL_Log("%s: couldn't create texture: %s", __func__, SDL_GetError()); - goto err1; - } - - Uint32 *pixels = calloc(1, dropper_srcrect.w * dropper_srcrect.h * 4); - if (pixels == NULL) { - SDL_Log("%s: pixels couldn't be allocated", __func__); - goto err2; - } - - SDL_SetRenderDrawColor(ren, 255, 255, 255, 255); - draw_circle(BALL_RADIUS + 5, BALL_RADIUS + 5, BALL_RADIUS + 5); - SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); - draw_circle(BALL_RADIUS + 5, BALL_RADIUS + 5, BALL_RADIUS); - SDL_RenderReadPixels(ren, &dropper_srcrect, PF_RGBA32, pixels, pitch); - SDL_Log("%08x%08x%08x%08x", pixels[0], pixels[1], pixels[2], pixels[3]); - - SDL_UpdateTexture(tex, &dropper_srcrect, pixels, pitch); - - return tex; - -err3: - SDL_DestroyTexture(tex); - tex = NULL; -err2: - free(pixels); -err1: - return NULL; -} - -SDL_Rect dropper_dstrect; int main(int argc, char *argv[]) { @@ -255,8 +248,12 @@ main(int argc, char *argv[]) unsigned int then, now, delta; SDL_TimerID balltimer; SDL_Rect ballrect; + fluid_settings_t *fsettings; + fluid_audio_driver_t *fadriver; + int sfid; + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) { + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO) != 0) { SDL_Log("Unable to initialize SDL: %s", SDL_GetError()); return 1; } @@ -266,25 +263,54 @@ main(int argc, char *argv[]) SDL_Log("Unable to create window: %s", SDL_GetError()); goto err1; } + + fsettings = new_fluid_settings(); + if (!fsettings) { + SDL_Log("Unable to create fluid settings"); + goto err2; + } + + fsynth = new_fluid_synth(fsettings); + if (!fsynth) { + SDL_Log("Unable to create fluid synth"); + goto err3; + } + if ((sfid = fluid_synth_sfload(fsynth, "/home/nihal/instruments/soundfonts/free/Xylophone-MediumMallets-SF2-20200706/Xylophone-MediumMallets-20200706.sf2", true)) == FLUID_FAILED) { + SDL_Log("Unable to load soundfont"); + goto err4; + } + + fluid_sfont_t *sfont = fluid_synth_get_sfont_by_id(fsynth, sfid); + if (sfont == NULL) { + SDL_Log("coudn't load sfont by id"); + return 1; + } + + fluid_sfont_iteration_start(sfont); + fluid_preset_t *fpreset = fluid_sfont_iteration_next(sfont); + int bank = fluid_preset_get_banknum(fpreset); + int prog = fluid_preset_get_num(fpreset); + fluid_synth_activate_tuning(fsynth, 0, bank, prog, true); + + fluid_settings_setstr(fsettings, "audio.driver", "sdl2"); + fadriver = new_fluid_audio_driver(fsettings, fsynth); + if (!fadriver) { + SDL_Log("Unable to create fluid synth driver"); + goto err5; + } + ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); if (ren == NULL) { SDL_Log("Unable to create renderer: %s", SDL_GetError()); - goto err2; + goto err6; } SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); - - SDL_Texture *dropper_texture = render_dropper(); - if (dropper_texture == NULL) { - SDL_Log("Unable to create dropper texture: %s", SDL_GetError()); - goto err3; - } - SDL_SetTextureBlendMode(dropper_texture, SDL_BLENDMODE_BLEND); - dropper_dstrect = (SDL_Rect){ dropper.x - (BALL_RADIUS + 5), dropper.y - (BALL_RADIUS + 5), 2*BALL_RADIUS+10, 2*BALL_RADIUS+10 }; balltimer = SDL_AddTimer(DROPRATE, setdropball, &dropball); + then = SDL_GetTicks(); running = true; SDL_Event e; while (running) { @@ -337,6 +363,11 @@ main(int argc, char *argv[]) continue; } + for (struct line *line = lines_first; line != NULL; line = line->next) { + if (ball_bounce(ball, line)) // returns true if it has bounced, can only bounce off of one line. + break; + } + ball_update(ball, delta); } @@ -344,19 +375,23 @@ main(int argc, char *argv[]) SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); SDL_RenderClear(ren); - SDL_RenderCopy(ren, dropper_texture, &dropper_srcrect, &dropper_dstrect); + aaFilledEllipseColor(ren, dropper.x, dropper.y, BALL_RADIUS + 5, BALL_RADIUS + 5, 0xFFFFFFFF); + aaFilledEllipseColor(ren, dropper.x, dropper.y, BALL_RADIUS, BALL_RADIUS, 0xFF000000); SDL_SetRenderDrawColor(ren, 255, 255, 255, 255); for (struct line *line = lines_first; line != NULL; line = line->next) { - draw_line(*line); + thickLineColor(ren, line->start.x, line->start.y, line->end.x, line->end.y, 3, 0xFFFFFFFF); } - if (ismousedown) - SDL_RenderDrawLine(ren, mousedown.x, mousedown.y, e.button.x, e.button.y); + if (ismousedown) { + aalineColor(ren, mousedown.x, mousedown.y-2, e.button.x, e.button.y-2, 0xFFFFFFFF); + aalineColor(ren, mousedown.x, mousedown.y+2, e.button.x, e.button.y+2, 0xFFFFFFFF); + thickLineColor(ren, mousedown.x, mousedown.y, e.button.x, e.button.y, 3, 0xFFFFFFFF); + } for (struct ball *ball = balls_first; ball != NULL; ball = ball->next) { - draw_circle(ball->x, ball->y, BALL_RADIUS); + aaFilledEllipseColor(ren, ball->x, ball->y, BALL_RADIUS, BALL_RADIUS, 0xFFFFFFFF); } SDL_RenderPresent(ren); @@ -364,9 +399,16 @@ main(int argc, char *argv[]) SDL_RemoveTimer(balltimer); - SDL_DestroyTexture(dropper_texture); -err3: +err7: SDL_DestroyRenderer(ren); +err6: + delete_fluid_audio_driver(fadriver); +err5: + fluid_synth_sfunload(fsynth, sfid, true); +err4: + delete_fluid_synth(fsynth); +err3: + delete_fluid_settings(fsettings); err2: SDL_DestroyWindow(win); err1: