first commit
This commit is contained in:
commit
1d0b47a9e2
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/chord
|
||||
*.o
|
14
Makefile
Normal file
14
Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
CC ?= cc
|
||||
CFLAGS= -Wfatal-errors -Wall -Wextra $(shell pkgconf --cflags portaudio-2.0)
|
||||
LDFLAGS= -s -lm $(shell pkgconf --libs portaudio-2.0)
|
||||
OBJS= main.o audio.o
|
||||
|
||||
all: chord
|
||||
chord: $(OBJS)
|
||||
$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS)
|
||||
%.o: %.c %.h
|
||||
$(CC) -c $< -o $@ $(CFLAGS)
|
||||
%.o: %.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS)
|
||||
clean:
|
||||
$(RM) *.o $(OBJS) chord
|
31
audio.c
Normal file
31
audio.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#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<unistd.h>
|
||||
#include"audio.h"
|
||||
|
||||
Audio aud_new(size_t samplerate,size_t nsamples,size_t channels)
|
||||
{
|
||||
Audio aud=(Audio){0};
|
||||
aud.samplerate=samplerate;
|
||||
aud.nsamples=nsamples;
|
||||
aud.channels=channels;
|
||||
aud.data=malloc(nsamples*sizeof(uint16_t));
|
||||
return aud;
|
||||
}
|
||||
|
||||
void aud_free(Audio*aud)
|
||||
{
|
||||
if(!aud)return;
|
||||
if(aud->data)
|
||||
free(aud->data);
|
||||
aud->data=0;
|
||||
*aud=(Audio){0};
|
||||
}
|
24
audio.h
Normal file
24
audio.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#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<unistd.h>
|
||||
|
||||
typedef struct Audio
|
||||
{
|
||||
size_t samplerate;
|
||||
size_t nsamples;
|
||||
size_t channels;
|
||||
uint16_t*data;
|
||||
} Audio;
|
||||
|
||||
Audio aud_new(size_t samplerate,size_t nsamples,size_t channels);
|
||||
void aud_free(Audio*aud);
|
233
main.c
Normal file
233
main.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
/***************
|
||||
* 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-z][#b]?[0-9][m]?\n"
|
||||
|
||||
// Audio data manipulation
|
||||
#define SEMITONE_CONSTANT 1.0594630943592953 // pow(2,1/12.0)
|
||||
#define raisesemitone(freq,nsemitones) ((freq)*pow(SEMITONE_CONSTANT,(nsemitones)))
|
||||
|
||||
// Chord generators
|
||||
#define makechord(aud,freq,...) do{double _f=nametofreq(freq);int _l[]={__VA_ARGS__};tri(aud,_f,GEN);for(size_t i=0;i<sizeof(_l)/sizeof(int);++i)tri(aud,raisesemitone(_f,_l[i]),ADD);}while(0)
|
||||
#define chord makechord
|
||||
#define M7(aud,f) makechord(aud,f,4,7,11)
|
||||
#define f7(aud,f) makechord(aud,f,4,7,10)
|
||||
#define m7(aud,f) makechord(aud,f,3,7,10)
|
||||
#define d7(aud,f) makechord(aud,f,3,6,10)
|
||||
|
||||
enum{GEN,ADD};
|
||||
|
||||
double nametofreq(const char*name);
|
||||
void avg(Audio*aud,size_t blocksize);
|
||||
void cleanup(int sig);
|
||||
void rnd(Audio*aud,size_t mode);
|
||||
void tri(Audio*aud,double freq,size_t mode);
|
||||
void zero(Audio*aud);
|
||||
void env(Audio*aud);
|
||||
|
||||
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);
|
||||
}
|
||||
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);env(&aud);play();
|
||||
chord(&aud, "g#4",3,6,9,13);env(&aud);play();
|
||||
chord(&aud, "f#4",3,7,10,14);env(&aud);play();
|
||||
chord(&aud, "e4",4,7,10,14);env(&aud);play();
|
||||
chord(&aud, "d4",4,7,11,14);env(&aud);play();
|
||||
chord(&aud, "c#4",3,7,10,13);env(&aud);play();
|
||||
chord(&aud, "b3",3,7,10,14);env(&aud);play();
|
||||
chord(&aud, "a3",4,7,11,14);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);env(&aud);play();
|
||||
}
|
||||
else
|
||||
{
|
||||
chord(&aud,argv[i],4,7,11,14);env(&aud);play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup(0);
|
||||
}
|
||||
|
||||
/*******
|
||||
* Convert note name into frequency
|
||||
*******/
|
||||
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;
|
||||
}
|
||||
|
||||
/*******
|
||||
* Convolver (low-pass filter)
|
||||
*******/
|
||||
void avg(Audio*aud,size_t blocksize)
|
||||
{
|
||||
if(!aud->data||!aud->nsamples||!aud->channels||!aud->samplerate||!blocksize)return;
|
||||
for(size_t i=blocksize-1;i<aud->nsamples;++i)
|
||||
{
|
||||
uint16_t sum=0;
|
||||
for(size_t j=0;j<blocksize&&(i+j)<aud->nsamples;++j)
|
||||
sum+=aud->data[i+j];
|
||||
/* for(size_t ch=0;ch<aud->channels;++ch,++i) */
|
||||
aud->data[i]=sum/blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
/*******
|
||||
* Randomize data
|
||||
*******/
|
||||
void rnd(Audio*aud,size_t mode)
|
||||
{
|
||||
if(!aud->data||!aud->nsamples||!aud->samplerate||!aud->channels)return;
|
||||
|
||||
for(size_t i=0;i<aud->nsamples;++i)
|
||||
{
|
||||
/* for(size_t ch=0;ch<aud->channels;++ch,++i) */
|
||||
uint16_t sample=rand();
|
||||
if(mode==GEN)
|
||||
aud->data[i]=sample;
|
||||
else if(mode==ADD)
|
||||
aud->data[i]=(aud->data[i]+sample)/2;
|
||||
}
|
||||
}
|
||||
|
||||
/*******
|
||||
* Triangle waveform generator
|
||||
*******/
|
||||
void tri(Audio*aud,double freq,size_t mode)
|
||||
{
|
||||
if(!aud->data||!aud->nsamples||!aud->samplerate||!aud->channels)return;
|
||||
for(size_t i=0;i<aud->nsamples;++i)
|
||||
{
|
||||
/* for(size_t ch=0;ch<aud->channels;++ch,++i) */
|
||||
//aud->data[i]=sin(i);
|
||||
uint16_t sample=(i%(int)(aud->samplerate/freq))*7;
|
||||
if(mode==GEN)
|
||||
aud->data[i]=sample;
|
||||
else if(mode==ADD)
|
||||
aud->data[i]=(aud->data[i]+sample)/2;
|
||||
}
|
||||
}
|
||||
|
||||
/*******
|
||||
* Zero all data
|
||||
*******/
|
||||
void zero(Audio*aud)
|
||||
{
|
||||
if(!aud->data||!aud->nsamples||!aud->channels)return;
|
||||
memset(aud->data,0,aud->nsamples*sizeof(uint16_t));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void env(Audio*aud)
|
||||
{
|
||||
if(!aud)return;
|
||||
if(!aud->data)return;
|
||||
size_t range=aud->nsamples/10;
|
||||
for(size_t i=0;i<range;++i)
|
||||
aud->data[i]*=(float)i/range;
|
||||
for(size_t i=0;i<aud->nsamples/8;++i)
|
||||
aud->data[aud->nsamples-1-i]*=(float)i/range;
|
||||
}
|
67
z
Normal file
67
z
Normal file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/ruby
|
||||
# Prototype for converting note names to
|
||||
# frequencies
|
||||
|
||||
def raise_semitone(freq,nsemitones)
|
||||
semitone_constant=2**(1/12.0) #1.059463
|
||||
freq*semitone_constant**nsemitones
|
||||
end
|
||||
|
||||
def getfreq(name)
|
||||
a0=27.5
|
||||
semi=0
|
||||
semi_name={'a'=>0,'b'=>2,'c'=>3,'d'=>5,'e'=>7,'f'=>8,'g'=>10}
|
||||
str_pos=1
|
||||
|
||||
if name[1]=='#'
|
||||
semi+=1
|
||||
str_pos=2
|
||||
elsif name[1]=='b'
|
||||
semi-=1
|
||||
str_pos=2
|
||||
end
|
||||
|
||||
oct=name[str_pos..].to_i
|
||||
if name[0]!='a'&&name[0]!='b'
|
||||
oct=(oct>0)?(oct-1):(0)
|
||||
end
|
||||
semi+=semi_name[name[0]]+oct*12
|
||||
|
||||
freq=raise_semitone(a0,semi)
|
||||
freq
|
||||
end
|
||||
|
||||
def test name,correct
|
||||
freq=getfreq name
|
||||
if freq.to_i==correct
|
||||
s=""
|
||||
else
|
||||
s="\t[FAIL: #{correct}]"
|
||||
end
|
||||
print "'#{name}':\t#{freq.to_i}#{s}\n"
|
||||
end
|
||||
|
||||
def main
|
||||
test 'a0',27
|
||||
test 'a1',55
|
||||
test 'a2',110
|
||||
test 'a3',220
|
||||
test 'a4',440
|
||||
test 'a#4',466
|
||||
test 'bb4',466
|
||||
test 'a5',880
|
||||
test 'a6',1760
|
||||
test 'c3',130
|
||||
test 'g6',1567
|
||||
test 'e1',41
|
||||
test 'e2',82
|
||||
test 'e3',164
|
||||
test 'e4',329
|
||||
test 'e5',659
|
||||
test 'e6',1318
|
||||
test 'f#4',369
|
||||
test 'd7',2349
|
||||
# test 'a10',99999
|
||||
end
|
||||
|
||||
main
|
Loading…
Reference in New Issue
Block a user