chord/main.c
2024-05-15 07:28:47 -05:00

153 lines
3.2 KiB
C

/***************
* Generate chords
* and play them
***************/
#include<ctype.h>
#include<fcntl.h>
#include<math.h>
#include<portaudio.h>
#include<stdbool.h>
#include<stdint.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<signal.h>
#include<unistd.h>
#include"audio.h"
#define USAGE "chord [CHORDs]\n"\
"\tCHORD\t[a-g][#b]?[0-9][m]?\n"
// Chord generator macros
#define chord(aud,f,...) makechord(aud,nametofreq(f),__VA_ARGS__)
#define M7(aud,f) chord(aud,f,4,7,11)
#define f7(aud,f) chord(aud,f,4,7,10)
#define m7(aud,f) chord(aud,f,3,7,10)
#define d7(aud,f) chord(aud,f,3,6,10)
static double nametofreq(const char*name);
static void cleanup(int sig);
static Audio aud=(Audio){0};
static PaStream*pa=NULL;
/*******
* Entry point
*******/
int main(int argc,char**argv)
{
signal(SIGINT,cleanup);
aud=aud_new(44100,22050,1);
if(!aud.data)
{
fprintf(stderr,"error: could not initialize Audio struct\n");
cleanup(0);
exit(0);
}
aud_zero(&aud);
Pa_Initialize();
Pa_OpenDefaultStream(&pa,0,aud.channels,paInt16,aud.samplerate,aud.nsamples,NULL,NULL);
Pa_StartStream(pa);
// Macro to play audio
#define play() do{Pa_WriteStream(pa,aud.data,aud.nsamples);usleep((((float)aud.nsamples)/aud.samplerate)*1000000);}while(0)
// Play pre-designed chord scale
if(argc<2)
{
// Play chord scale
chord(&aud, "a4",4,7,11,14);aud_env(&aud);play();
chord(&aud, "g#4",3,6,9,13);aud_env(&aud);play();
chord(&aud, "f#4",3,7,10,14);aud_env(&aud);play();
chord(&aud, "e4",4,7,10,14);aud_env(&aud);play();
chord(&aud, "d4",4,7,11,14);aud_env(&aud);play();
chord(&aud, "c#4",3,7,10,13);aud_env(&aud);play();
chord(&aud, "b3",3,7,10,14);aud_env(&aud);play();
chord(&aud, "a3",4,7,11,14);aud_env(&aud);play();
//Pa_Sleep((((float)aud.nsamples)/aud.samplerate)*8);
}
// Play command-line supplied chord names
else
{
for(size_t i=1;i<(size_t)argc;++i)
{
// Option
if(argv[i][0]=='-')
{
if(strcmp(argv[i],"--help")==0)
printf("%s",USAGE);
}
// Chord specification
else
{
printf("playing '%s'\n",argv[i]);
if(argv[i][strlen(argv[i])-1]=='m')
{
chord(&aud,argv[i],3,7,10,14);aud_env(&aud);play();
}
else
{
chord(&aud,argv[i],4,7,11,14);aud_env(&aud);play();
}
}
}
}
cleanup(0);
}
/*******
* Convert note name into frequency
*******/
static double nametofreq(const char*name)
{
char letter='a';
double a0=27.5;
double freq=0.0L;
size_t letter_offsets[]={0,2,3,5,7,8,10};
size_t octave=0;
size_t semi=0;
size_t str_pos=1;
if(!name)return freq;
if(!name[0])return freq;
if(!name[1])return freq;
letter=name[0];
if(name[1]=='#')
++semi,str_pos=2;
else if(name[1]=='b')
--semi,str_pos=2;
if(!name[str_pos])return freq;
octave=atoi(name+str_pos);
if(letter!='a'&&letter!='b')
octave=(octave>0)?(octave-1):0;
semi+=letter_offsets[letter-'a']+octave*12;
freq=raisesemitone(a0,semi);
return freq;
}
/*******
* Free resources and quit
*******/
static void cleanup(int sig)
{
if(sig==SIGINT)
puts("exiting due to SIGINT");
/* else */
/* puts("bye"); */
Pa_AbortStream(pa);
Pa_CloseStream(pa);
Pa_Terminate();
aud_free(&aud);
}