first commit

This commit is contained in:
corey 2024-06-15 16:56:16 -05:00
commit 3abde03ff7
12 changed files with 566 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
/convotest

BIN
DejaVuSerif.ttf Normal file

Binary file not shown.

16
Makefile Normal file
View File

@ -0,0 +1,16 @@
CFLAGS= -Wfatal-errors -Wall -Wextra $(shell pkgconf --cflags sdl2 SDL2_ttf)
LDFLAGS= -s -lm $(shell pkgconf --libs sdl2 SDL2_ttf)
OBJS= main.o sy.o button.o tools.o
all: convotest
convotest: $(OBJS)
$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS)
%.o: %.c %.h
$(CC) -c $< -o $@ $(CFLAGS) $(LDFLAGS)
%.o: %.c
$(CC) -c $^ -o $@ $(CFLAGS) $(LDFLAGS)
%.o: %.asm
nasm $(NASMFLAGS) $^ -o $@
clean:
$(RM) *.o $(OBJS) convotest

13
button.c Normal file
View File

@ -0,0 +1,13 @@
#include<SDL2/SDL.h>
#include"button.h"
#include"tools.h"
void button_draw(SDL_Renderer*ren,TTF_Font*font,Button*button)
{
if(!ren)return;
if(!button)return;
SDL_Rect r={.x=button->x,.y=button->y,.w=button->w,.h=button->h};
SDL_SetRenderDrawColor(ren,0,0,128,255);
SDL_RenderFillRect(ren,&r);
print_text(ren,font,0xffffff,button->x,button->y,button->label);
}

15
button.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
typedef struct Button
{
const char*label;
size_t x;
size_t y;
size_t w;
size_t h;
} Button;
void button_draw(SDL_Renderer*ren,TTF_Font*font,Button*button);

222
main.c Normal file
View File

@ -0,0 +1,222 @@
#include<assert.h>
#include<inttypes.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
#include<SDL2/SDL_image.h>
#include"sy.h"
#include"wav.h"
#include"button.h"
#include"tools.h"
#define max(x,y) (((x)>(y))?(x):(y))
#define min(x,y) (((x)<(y))?(x):(y))
#define WIDTH 320
#define HEIGHT 240
enum{BLP,BNS,BTR,BSQ,BSN};
int main(void)
{
SDL_Event event;
SDL_Renderer*ren=NULL;
SDL_Window*win=NULL;
TTF_Font*font=NULL;
Button buttons[16];
bool running=true;
const size_t samplerate=44100;
const size_t waveform_size=1024;
float waveform[waveform_size];
size_t filt_cutoff=500;
size_t nbuttons=0;
size_t osc_freq=440;
// Init SDL2
if(SDL_Init(SDL_INIT_EVERYTHING)<0)
{
fprintf(stderr,"error: failed to initialize SDL2\n");
goto quit;
}
// Init TTF
TTF_Init();
font=TTF_OpenFont("DejaVuSerif.ttf",12);
if(!font)
{
fprintf(stderr,"error: failed initialize SDL2 TTF\n");
goto quit;
}
// Create window
win=SDL_CreateWindow("ok",SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WIDTH,HEIGHT,0);
if(!win)
{
fprintf(stderr,"error: failed to create window\n");
goto quit;
}
// Create renderer
ren=SDL_CreateRenderer(win,-1,0);
if(!ren)
{
fprintf(stderr,"error: failed to create renderer\n");
goto quit;
}
// Clear window
SDL_SetRenderDrawColor(ren,0,0,0,0);
SDL_RenderClear(ren);
SDL_RenderPresent(ren);
// Create buttons
#define create_button(n,_label) do{buttons[n]=(Button){.label=(_label),.x=nbuttons*25+5,.y=HEIGHT-20,.w=20,.h=16};++nbuttons;}while(0)
create_button(BLP,"LP");
create_button(BNS,"Ns");
create_button(BTR,"Tr");
create_button(BSQ,"Sq");
create_button(BSN,"Sn");
#undef create_button
// Initialize data
srand(time(NULL));
nois(waveform,waveform_size);
// Main loop
while(running)
{
if(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
running=false;
break;
// Mouse input
case SDL_MOUSEBUTTONDOWN:
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
// Check each button
for(size_t i=0;i<nbuttons;++i)
if(event.button.x>(int)buttons[i].x&&
event.button.x<(int)buttons[i].x+(int)buttons[i].w&&
event.button.y>(int)buttons[i].y&&
event.button.y<(int)buttons[i].y+(int)buttons[i].h)
{
switch(i)
{
case BLP:
float windowsize=(((float)samplerate)/filt_cutoff);
/* printf("%luHz: %g\n",filt_cutoff,windowsize); */
/* printf("%luHz(44.1kHz): %g\n",filt_cutoff,((float)44100)/filt_cutoff); */
avgwindow(waveform,waveform_size,windowsize);
break;
case BNS:nois(waveform,waveform_size);break;
case BTR:tri(waveform,waveform_size,osc_freq);break;
case BSQ:sq(waveform,waveform_size,osc_freq);break;
case BSN:sn(waveform,waveform_size,osc_freq);break;
}
/* printf("'%s' pressed\n",buttons[i].label); */
break; // Do not click more than one button
}
break;
}
break;
// Keyboard input
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
// UI
case SDLK_ESCAPE:
case SDLK_q:
running=false;
break;
// Oscillators
case SDLK_n:nois(waveform,waveform_size);break;
case SDLK_t:tri(waveform,waveform_size,osc_freq);break;
case SDLK_e:sq(waveform,waveform_size,osc_freq);break;
case SDLK_i:sn(waveform,waveform_size,osc_freq);break;
// Filters, envelopes, etc.
case SDLK_s:sort(waveform,waveform_size);break;
case SDLK_r:shuf(waveform,waveform_size);break;
case SDLK_y:atk(waveform,waveform_size,5);break;
case SDLK_u:rel(waveform,waveform_size,10);break;
case SDLK_o:rotl(waveform,waveform_size);break;
case SDLK_a:
{
float windowsize=(((float)samplerate)/filt_cutoff);
/* printf("%luHz: %g\n",filt_cutoff,windowsize); */
/* printf("%luHz(44.1kHz): %g\n",filt_cutoff,((float)44100)/filt_cutoff); */
avgwindow(waveform,waveform_size,windowsize);
}
break;
// Parameters
case SDLK_UP:osc_freq+=(1/12.0)*osc_freq;break;
case SDLK_DOWN:osc_freq-=(1/12.0)*osc_freq;if(osc_freq<1)osc_freq=1;break;
case SDLK_EQUALS:filt_cutoff+=(1/12.0)*filt_cutoff;break;
case SDLK_MINUS:filt_cutoff-=(1/12.0)*filt_cutoff;if(filt_cutoff<1)filt_cutoff=1;break;
}
break;
default:
break;
}
}
// Render
SDL_SetRenderDrawColor(ren,0,0,0,255);
SDL_RenderClear(ren);
SDL_SetRenderDrawColor(ren,255,0,0,255);
for(size_t i=0;i<WIDTH;++i)
SDL_RenderDrawLine(ren,i,HEIGHT/2,i,max(HEIGHT/2-waveform[i*(waveform_size/WIDTH)]*100,0));
for(size_t i=0;i<nbuttons;++i)
button_draw(ren,font,&buttons[i]);
ttfprintf(ren,font,0xffffff,5,5,
"sr:%lu ns:%lu f:%lu co:%lu",
samplerate,
waveform_size,
osc_freq,
filt_cutoff
);
SDL_RenderPresent(ren);
SDL_Delay(20);
//usleep(20000);
}
// Clean up, exit
quit:
if(win)
SDL_DestroyWindow(win);
if(ren)
SDL_DestroyRenderer(ren);
if(font)
TTF_CloseFont(font);
SDL_Quit();
TTF_Quit();
}

19
readme Normal file
View File

@ -0,0 +1,19 @@
Keyboard keys:
GENERATOR/OSCILLATORS:
E: Generate square wave
I: Generate sine wave
N: Generate noise wave
T: Generate triangle wave
WAVEFORM ACTIONS:
A: Average (convolution low-pass)
R: Shuffle (randomize)
S: Sort
O: Rotate left
PARAMETER MODIFICATION:
+/=: Increase filter cutoff frequency
-/_: Decrease filter cutoff frequency
Up: Increase generator frequency
Down: Decrease generator frequency

176
sy.c Normal file
View File

@ -0,0 +1,176 @@
#include"sy.h"
void nois(float*a,size_t n)
{
int amp=2;
for(size_t i=0;i<n;++i)
a[i]=frand()*amp-amp/2;
}
// triangle oscillator
void tri(float*a,int n,int freq)
{
int period=44100.0/freq;
int amp=2;
float ratio=amp/((float)period/2);
for(int j=0;j<n;++j)
{
int i=j+period/4; // adjust phase
if(i%period<period/2)
a[j]=i%period;
else
a[j]=period-(i%period);
// a[i] is half of a ratio
// and needs to be adjusted
// for amplitude
a[j]*=ratio;
a[j]-=amp/2;
}
}
// square oscillator
void sq(float*a,int n,int freq)
{
int period=44100.0/freq;
for(int i=0;i<n;++i)
{
if(i%period<period/2)
a[i]=1;
else
a[i]=-1;
}
}
// sine oscillator
void sn(float*a,int n,int freq)
{
int period=44100.0/freq;
for(int i=0;i<n;++i)
a[i]=sin(2*(M_PI)*((float)i/period));
}
void atk(float*a,size_t n,size_t t)
{
if(!a)return;
for(size_t i=0;i<t&&i<n;++i)
a[i]*=i/((float)t);
}
void rel(float*a,size_t n,size_t t)
{
if(!a)return;
for(size_t i=1;i<t&&i<n;++i)
a[n-i]*=i/((float)t);
}
// Convolving average lowpass filter
// Smooth high frequencies
// samplerate: 44100 Hz
// cutoff: 440 Hz
// window: 44100/440=100
void avgwindow(float*a,int n,int windowsize)
{
if(!a)return;
for(int k=0;k<n;++k)
{
int i=k-windowsize/2;
float sum=0;
while(i<0)i+=n;
for(int j=0;j<windowsize;++j)
{
sum+=a[i];
i=(i+1)%n;
}
a[k]=sum/windowsize;
}
}
/* void avg(float*a,size_t n,float w) */
/* { */
/* if(!w)return; */
/* if(!a)return; */
/* w=ceil(w)+1; */
/* for(size_t i=0;i<n;++i) */
/* { */
/* if(i<w) */
/* for(size_t j=0;j<w;++j) */
/* { */
/* if(j<i)a[i]+=a[j]; */
/* } */
/* else */
/* for(size_t j=i-w+1;j<i&&j<n;++j) */
/* a[i]+=a[j]; */
/* a[i]/=w; */
/* } */
/* } */
void printwindow(float*a,int n,int windowsize)
{
if(!a)return;
int i=n-windowsize/2;
if(i<0)i+=n;
for(int j=0;j<windowsize;++j)
{
printf("%f",a[i]);
if(j<windowsize-1)
printf(", ");
i=(i+1)%n;
}
printf("\n");
}
void printwindowall(float*a,int n,int windowsize)
{
if(!a)return;
for(int k=0;k<n;++k)
{
int i=k-windowsize/2;
while(i<0)i+=n;
printf("(");
for(int j=0;j<windowsize;++j)
{
printf("%f",a[i]);
if(j<windowsize-1)
printf(", ");
i=(i+1)%n;
}
printf(")\n");
}
}
// Insert integer into correct place
// starting from 0 to n
void sort(float*a,size_t n)
{
float t;
if(!a)return;
for(size_t i=0;i<n-1;++i)
for(size_t j=i+1;j<n;++j)
if(a[i]>a[j])
t=a[i],
a[i]=a[j],
a[j]=t;
}
void shuf(float*a,size_t n)
{
float t;
for(size_t i=0;i<n;++i)
{
size_t sw=rand()%n;
t=a[i],
a[i]=a[sw],
a[sw]=t;
}
}
void rotl(float*a,size_t n)
{
if(!a)return;
float t=a[0];
for(size_t i=1;i<n;++i)
a[i-1]=a[i];
a[n-1]=t;
}

26
sy.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include<assert.h>
#include<inttypes.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<math.h>
#define frand() ((float)(rand()%10000)/10000)
void atk(float*a,size_t n,size_t t);
void avgwindow(float*a,int n,int windowsize);
void nois(float*a,size_t n);
void printwindow(float*a,int n,int windowsize);
void printwindowall(float*a,int n,int windowsize);
void rel(float*a,size_t n,size_t t);
void rotl(float*a,size_t n);
void shuf(float*a,size_t n);
void sn(float*a,int n,int freq);
void sort(float*a,size_t n);
void sq(float*a,int n,int freq);
void tri(float*a,int n,int freq);

32
tools.c Normal file
View File

@ -0,0 +1,32 @@
#include<assert.h>
#include<inttypes.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
#include<SDL2/SDL_image.h>
#include"tools.h"
void print_text(SDL_Renderer*ren,TTF_Font*font,int color,int x,int y,const char*str)
{
SDL_Color c=(SDL_Color){
.r=(color>>16)&0xff,
.g=(color>>8)&0xff,
.b=(color>>0)&0xff,
.a=0xff,
};
SDL_Surface*surface=TTF_RenderText_Solid(font,str,c);
SDL_Texture*texture=SDL_CreateTextureFromSurface(ren,surface);
SDL_Rect rect={.x=x,.y=y,.w=surface->w,.h=surface->h};
SDL_RenderCopy(ren,texture,NULL,&rect);
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
}

20
tools.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include<assert.h>
#include<inttypes.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<SDL2/SDL.h>
#include<SDL2/SDL_ttf.h>
#include<SDL2/SDL_image.h>
// SDL2_ttf printf-like macro
#define ttfprintf(ren,font,col,x,y,fmt,...) do{\
char _b[128];sprintf(_b,fmt,__VA_ARGS__);\
print_text(ren,font,col,x,y,_b);}while(0)
void print_text(SDL_Renderer*ren,TTF_Font*font,int color,int x,int y,const char*str);

25
wav.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include<stdint.h>
#include<stdlib.h>
#include<string.h>
// Struct to contain .WAV file header data for exporting .WAV files
typedef struct WAVE
{
int8_t riff[4]; // "RIFF" mark
uint32_t filesize; // File size
int8_t wave[4]; // "WAVE" mark
int8_t fmt[4]; // "fmt " mark
uint32_t formatdatalength; // Length of format data
uint16_t formattype; // Type (PCM for our purpose)
uint16_t channels; // Mono/stereo
uint32_t samplerate; // Samples per second
uint32_t bitspersecond; // Samplerate * Bits per sample
uint16_t bittype; // 1=>8bit mono, 2=>8bit stereo, 4=>16bit stereo...
uint16_t bitspersample; // e.g., 16, 32
int8_t data[4]; // "data" mark
uint32_t datalength; // Length of audio
} WAVE;
// Create a custom WAVE header data struct for .WAV files
WAVE wav_create(uint32_t samplerate,uint16_t bitspersample,uint16_t channels,uint32_t length);