first commit
This commit is contained in:
commit
3abde03ff7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.o
|
||||
/convotest
|
BIN
DejaVuSerif.ttf
Normal file
BIN
DejaVuSerif.ttf
Normal file
Binary file not shown.
16
Makefile
Normal file
16
Makefile
Normal 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
13
button.c
Normal 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
15
button.h
Normal 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
222
main.c
Normal 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
19
readme
Normal 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
176
sy.c
Normal 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
26
sy.h
Normal 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
32
tools.c
Normal 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
20
tools.h
Normal 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
25
wav.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user