diff --git a/firmware/Makefile b/firmware/Makefile index 76d9be1..27cba38 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -35,7 +35,7 @@ OPT = s ########################################################################################################## # List C source files here. (C dependencies are automatically generated.) -SRC = main.c synth.c +SRC = main.c synth.c usart.c ########################################################################################################## diff --git a/firmware/main.c b/firmware/main.c index 04da2ca..74ce931 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -6,7 +6,7 @@ #include "main.h" #include "synth.h" - +#include "usart.h" static void init_sampletimer(void) { @@ -18,7 +18,7 @@ static void init_sampletimer(void) TCCR0A = (1 << WGM01); TCCR0B = (1 << CS00) | (1 << CS01); - OCR0A = 3; /* TOP */ + OCR0A = 6; /* TOP */ TCNT0 = 0; /*enable interrupt*/ TIMSK0 |= (1< +#include "synth.h" #include "freq_table.h" // sample rate is 8M / (3 * 64) - -enum { WAVE_OFF, WAVE_PULSE, WAVE_SAW, WAVE_NOISE }; enum { channel_count = 3, - tick_length = 800, + tick_length = 400, row_length = 4, pattern_length = 16 }; -typedef struct { - uint8_t note; - uint8_t inst_nr; - uint8_t pos; - - uint16_t phase; - uint16_t pulse_width; - - uint8_t level; // envelop level - -} synth_channel_t; - - -typedef struct { - uint16_t pulse_width; - uint8_t pulse_sweep; - uint8_t wave_table_pos; - uint8_t decay; - -} synth_instrument_t; static const synth_instrument_t instruments[] = { @@ -68,9 +47,9 @@ static const uint8_t patterns[][pattern_length][2] = { {}, { { 33 - 12, 0 }, + { 0, 0 }, { 0xff, 1 }, - { 33 - 12, 1 }, - { 0xff, 1 }, + { 0, 0 }, { 33, 1 }, { 0xff, 1 }, { 33, 1 }, @@ -86,9 +65,9 @@ static const uint8_t patterns[][pattern_length][2] = { }, { { 28 - 12, 0 }, + { 0, 0 }, { 0xff, 1 }, - { 28 - 12, 1 }, - { 0xff, 1 }, + { 0, 0 }, { 28, 1 }, { 0xff, 1 }, { 28, 1 }, @@ -202,14 +181,6 @@ void synth_init(void) tick = 0; row = 0; seq = 0; - -/* - // some test values - channels[0].wave = WAVE_PULSE; - channels[0].pulse_width = 1 << 15; - - channels[1].wave = WAVE_SAW; -*/ } uint16_t synth_mix(void) @@ -239,7 +210,7 @@ uint16_t synth_mix(void) if(note == 0xff) { chan->level = 0; } else { - chan->level = 100; + chan->level = 80; // TODO: less? chan->note = note; chan->inst_nr = patterns[pattern_nr][row][1]; inst = &instruments[chan->inst_nr]; diff --git a/firmware/synth.h b/firmware/synth.h index 6b90c0f..24f0088 100644 --- a/firmware/synth.h +++ b/firmware/synth.h @@ -1,3 +1,26 @@ +enum { WAVE_OFF, WAVE_PULSE, WAVE_SAW, WAVE_NOISE }; + +typedef struct { + uint8_t note; + uint8_t inst_nr; + uint8_t pos; + + uint16_t phase; + uint16_t pulse_width; + + uint8_t level; // envelop level + +} synth_channel_t; + + +typedef struct { + uint16_t pulse_width; + uint8_t pulse_sweep; + uint8_t wave_table_pos; + uint8_t decay; + +} synth_instrument_t; + void synth_init(void); uint16_t synth_mix(void); diff --git a/firmware/usart.c b/firmware/usart.c new file mode 100644 index 0000000..0937238 --- /dev/null +++ b/firmware/usart.c @@ -0,0 +1,132 @@ +/* + * PentaFnord Firmware + * + * by Alexander Lorz + * + * + * The USART control code is derived by code from the 4CHLED project + * by sebseb7: https://github.com/sebseb7/eagle/tree/master/4CHLED/firmware + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include + +//#include "main.h" +#include "usart.h" + +#define UART_RXBUFSIZE 32 + +volatile static uint8_t rxbuf0[UART_RXBUFSIZE]; +volatile static uint8_t *volatile rxhead0, *volatile rxtail0; +//volatile uint8_t xon = 0; + + +ISR (USART_RX_vect) +{ + UCSR0B &= ~(1 << RXCIE0); + asm volatile("sei"); + + int diff; + uint8_t c; + c=UDR0; + diff = rxhead0 - rxtail0; + if (diff < 0) diff += UART_RXBUFSIZE; + if (diff < UART_RXBUFSIZE -1) + { + *rxhead0 = c; + ++rxhead0; + if (rxhead0 == (rxbuf0 + UART_RXBUFSIZE)) rxhead0 = rxbuf0; +// if((diff > 100)&&(xon==0)) +// { +// xon=1; +// //set the CTS pin +// } + } + UCSR0B |= (1 << RXCIE0); +} + + +void USART0_Init (void) +{ + // set baudrate + #undef BAUD + #define BAUD 115200 + #include + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + + //#if USE_2X + UCSR0A |= (1 << U2X0); // enable double speed operation + //#else + // UCSR0A &= ~(1 << U2X0); // disable double speed operation + //#endif + + + // flush receive buffer + while ( UCSR0A & (1 << RXC0) ) UDR0; + + // set 8N1 + UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); + UCSR0B &= ~(1 << UCSZ02); + + UCSR0B |= (1 << RXEN0)|(1 << TXEN0); //enable send and receive + + UCSR0B |= (1 << RXCIE0); //enable receive interrup + + rxhead0 = rxtail0 = rxbuf0; + +} + + + +void USART0_putc (char c) +{ + + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; +} + + +uint8_t USART0_Getc_nb(uint8_t *c) +{ + if (rxhead0==rxtail0) return 0; + *c = *rxtail0; + if (++rxtail0 == (rxbuf0 + UART_RXBUFSIZE)) rxtail0 = rxbuf0; + +// uint8_t diff = rxhead0 - rxtail0; +// if((diff < 10)&&(xon==1)) +// { +// xon=0; +// //set the CTS pin +// } + + return 1; +} + +void USART0_crlf(){ + USART0_putc(0x0A); //newline + USART0_putc(0x0D); //carriage return +}; + +void USART0_put_uint8(uint8_t x){ + USART0_putc(((x & 0b11110000)>>4)+0x30); + USART0_putc((x & 0b00001111)+0x30); + +} +void USART0_put_uint16(uint16_t x){ + USART0_put_uint8 ((x & 0xFF00)>>8); + USART0_put_uint8 (x & 0x00FF); + +} diff --git a/firmware/usart.h b/firmware/usart.h new file mode 100644 index 0000000..d0ddebc --- /dev/null +++ b/firmware/usart.h @@ -0,0 +1,34 @@ +/* + * PentaFnord Firmware + * + * by Alexander Lorz + * + * + * The USART control code is derived by code from the 4CHLED project + * by sebseb7: https://github.com/sebseb7/eagle/tree/master/4CHLED/firmware + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _USART_H +#define _USART_H + +void USART0_Init (void); +void USART0_putc (char c); +uint8_t USART0_Getc_nb(uint8_t*); +void USART0_put_uint8(uint8_t); +void USART0_crlf(); +void USART0_put_uint16(uint16_t); + +#endif + diff --git a/synth/make_freq_table.py b/synth/freq_table.py similarity index 100% rename from synth/make_freq_table.py rename to synth/freq_table.py diff --git a/synth/main.c b/synth/main.c index e699ac0..7ef00bb 100644 --- a/synth/main.c +++ b/synth/main.c @@ -1,29 +1,196 @@ #include #include #include +#include #include #include "synth.h" +enum { MIXRATE = 8000000 / (6 * 64) }; + +static SDL_AudioSpec spec = { MIXRATE, AUDIO_U16SYS, 1, 0, 1024, }; + static void fill_buffer(void* userdata, Uint8* stream, int len) { - for(int i = 0; i < len / 2; i++) ((uint16_t*)stream)[i] = synth_mix() * 50; + for(int i = 0; i < len / 2; i++) { + uint16_t m = synth_mix(); + assert(m < 0x100); + ((uint16_t*)stream)[i] = m * 50; + } } -SDL_AudioSpec spec = { 8000000 / (3 * 64), AUDIO_U16SYS, 1, 0, 1024, }; +// this parser is a complete hack +// put i can't care less right now +static int parse_tune(const char* filename) { + FILE* file = fopen(filename, "r"); + if(!file) return -1; -int main(int argc ,char** argv) { - synth_init(); + char wave_names[256][256]; + memset(wave_names, 0, sizeof(wave_names)); + int wave_counter = 0; + + + char inst_names[256][256]; + memset(inst_names, 0, sizeof(inst_names)); + int inst_counter = 0; + + + char pattern_names[256][256]; + memset(pattern_names, 0, sizeof(pattern_names)); + int pattern_counter = 0; + + tune_length = 0; + + + int state = 0; + int line_nr = 0; + char line[256]; + while(fgets(line, 256, file)) { + line_nr++; + + // skip empty line + if(line[0] == '\n') continue; + + // comments + if(line[0] == '#') continue; + + if(state == 0) { + if(strcmp("[WAVETABLE]\n", line)) return line_nr; + state = 1; + } + else if(state == 1) { + if(line[0] == '[') { + if(strcmp("[INSTRUMENTS]\n", line)) return line_nr; + state = 2; + } + else if(!isspace(line[0])) { + sscanf(line, "%s", wave_names[wave_counter]); + } + else if(line[0] == '\t') { + int a, b; + if(sscanf(line + 1, "%u %u", &a, &b) != 2) return line_nr; + wave_table[wave_counter][0] = a; + wave_table[wave_counter][1] = b; + wave_counter++; + } + else return line_nr; + } + else if(state == 2) { + if(line[0] == '[') { + if(strcmp("[PATTERNS]\n", line)) return line_nr; + state = 3; + } + else { + char wave[256]; + int pw, ps, d; + if(sscanf(line, "%s %u %u %u %s", + inst_names[inst_counter], &pw, &ps, &d, + wave) != 5) return line_nr; + + instruments[inst_counter].pulse_width = pw; + instruments[inst_counter].pulse_sweep = ps; + instruments[inst_counter].decay = d; + int i; + for(i = 0; i < 256; i++) + if(strcmp(wave_names[i], wave) == 0) break; + if(i == 256) return line_nr; + instruments[inst_counter].wave_table_pos = i; + inst_counter++; + } + } + else if(state == 3) { + if(line[0] == '[') { + if(strcmp("[PATTERNTABLE]\n", line)) return line_nr; + state = 4; + } + else { + if(isspace(line[0])) return line_nr; + sscanf(line, "%s", pattern_names[pattern_counter]); + for(int i = 0; i < pattern_length && fgets(line, 256, file); i++) { + line_nr++; + if(line[0] != '\t') return line_nr; + char note[256]; + char inst[256]; + int m = sscanf(line + 1, "%s %s", note, inst); + if(m == 0) return line_nr; + char* s = "ccddeffggaab"; + char* p = strchr(s, line[1]); + if(p) { + patterns[pattern_counter][i][0] = (p - s) + + (note[1] == '#') + (note[2] - '0') * 12; + int j; + for(j = 0; j < 256; j++) + if(strcmp(inst_names[j], inst) == 0) break; + if(j == 256) return line_nr; + patterns[pattern_counter][i][1] = j; + + } + else if(m == 1) { + if(strcmp("---", note) == 0) + patterns[pattern_counter][i][0] = 0xff; + else if(strcmp(".", note) == 0) + patterns[pattern_counter][i][0] = 0; + else return line_nr; + + } + else return line_nr; + + } + pattern_counter++; + } + + + + } + else { + char* p = line; + char pat[256]; + for(int i = 0; i < channel_count; i ++) { + sscanf(p, "%s", pat); + int j; + for(j = 0; j < 256; j++) + if(strcmp(pattern_names[j], pat) == 0) break; + if(j == 256) return line_nr; + pattern_table[tune_length][i] = j; + while(*p && !isspace(*p)) p++; + while(*p && isspace(*p)) p++; + } + tune_length++; + + + } + } + if(state =! 4) return line_nr; + + + fclose(file); + return 0; +} + + + + +int main(int argc, char** argv) { + if(argc != 2) { + printf("usage: %s tunefile\n", argv[0]); + return 0; + } + + int error = parse_tune(argv[1]); + if(error != 0) { + fprintf(stderr, "%d: parsing error\n", error); + return 1; + } spec.callback = &fill_buffer; if(SDL_OpenAudio(&spec, &spec) < 0) { fprintf(stderr, "ERROR"); exit(1); } - printf("freq = %d\n", spec.freq); SDL_PauseAudio(0); + puts("playing..."); getchar(); diff --git a/synth/simple.tune b/synth/simple.tune new file mode 100644 index 0000000..8f6aa06 --- /dev/null +++ b/synth/simple.tune @@ -0,0 +1,255 @@ +[WAVETABLE] +# note offset | wave form + +bass + 250 1 + 0 1 + 255 255 + +pad00 + 0 2 + 3 2 + 7 2 + 12 2 + 252 255 + +pad01 + 0 2 + 4 2 + 7 2 + 12 2 + 252 255 + + +kick + 255 1 + 251 1 + 246 1 + 242 1 + 0 255 + 255 255 + +snare + 0 3 + 0 1 + 254 1 + 248 1 + 244 1 + 0 3 + 255 255 + +lead + 0 2 + 255 255 + +lead_ + 255 2 + 0 2 + 255 255 + + +[INSTRUMENTS] +# pulse width | pulse sweep | decay | label in wave table + +bass 32768 10 4 bass + +pad00 32768 10 3 pad00 +pad01 32768 10 3 pad01 + +kick 32768 0 3 kick +snare 30000 0 10 snare + +lead 1000 255 1 lead +lead_ 1000 255 1 lead_ + +[PATTERNS] +# note | instrument +--- + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + +drum00 + g_3 kick + . + . + g_3 kick + d_4 snare + . + g_3 kick + . + . + . + . + . + d_4 snare + . + g_3 kick + . + +drum01 + g_3 kick + . + . + g_3 kick + d_4 snare + . + g_3 kick + . + . + . + . + . + d_4 snare + . + . + . + +drum02 + g_3 kick + . + . + g_3 kick + d_4 snare + . + g_3 kick + . + . + . + g_3 kick + . + d_4 snare + . + d_4 snare + d_4 snare + +bass00 + c_3 bass + --- + c_5 pad00 + c_3 bass + --- + . + c_3 bass + d#5 pad01 + c_3 bass + --- + . + . + a#4 pad01 + . + c_4 bass + c_4 bass + +bass01 + c_3 bass + --- + c_5 pad00 + c_3 bass + --- + . + c_3 + --- + c_3 + --- + . + . + a#4 pad01 + . + . + . + + +lead00 + . + . + g_5 lead + --- + g_5 lead_ + . + . + . + --- + . + f_5 lead + d#5 + f_5 + d#5 + f_5 + g_5 + +lead01 + f_5 lead + c_5 + --- + c_5 + . + . + --- + c_5 + d#5 + c_5 + f_5 + c_5 + g_5 + d#5 + a#5 + c_6 lead_ + +lead02 + . + . + g_5 lead + --- + g_5 lead_ + . + . + . + --- + . + f_5 lead + d#5 + f_5 lead_ + d#5 lead + a#4 + c_5 + + +[PATTERNTABLE] + + +--- bass00 --- +--- bass01 --- +--- bass00 --- +--- bass01 --- + +--- bass00 lead00 +--- bass01 lead01 +--- bass00 lead02 +--- bass01 --- + +drum00 bass00 lead00 +drum01 bass01 lead01 +drum00 bass00 lead02 +drum02 bass01 --- + +drum00 bass00 lead00 +drum01 bass01 lead01 +drum00 bass00 lead02 +drum02 bass01 --- + + + diff --git a/synth/synth.c b/synth/synth.c deleted file mode 120000 index 482a33e..0000000 --- a/synth/synth.c +++ /dev/null @@ -1 +0,0 @@ -../firmware/synth.c \ No newline at end of file diff --git a/synth/synth.c b/synth/synth.c new file mode 100644 index 0000000..eade3fe --- /dev/null +++ b/synth/synth.c @@ -0,0 +1,105 @@ +#include +#include "synth.h" +#include "freq_table.h" + + +synth_instrument_t instruments[256]; +uint8_t wave_table[256][2]; +uint8_t patterns[256][pattern_length][2]; +uint8_t pattern_table[256][channel_count]; +uint8_t tune_length; + +int16_t sample; +int8_t tick; +int8_t row; +int8_t seq; + + +static synth_channel_t channels[channel_count]; + + +uint16_t synth_mix(void) +{ + if(sample == 0) { // new tick + for(int i = 0; i < channel_count; i++) { + synth_channel_t* chan = &channels[i]; + + const synth_instrument_t* inst = &instruments[chan->inst_nr]; + + if(chan->level > inst->decay) chan->level -= inst->decay; + else chan->level = 0; + + chan->pulse_width += inst->pulse_sweep; + + + chan->pos++; + if(wave_table[chan->pos][1] == 0xff) chan->pos += wave_table[chan->pos][0]; + + + // enter new row + if(tick == 0) { + uint8_t pattern_nr = pattern_table[seq][i]; + uint8_t note = patterns[pattern_nr][row][0]; + + if(note) { // new note, maybe? + if(note == 0xff) { + chan->level = 0; + } else { + chan->level = 80; // TODO: less? + chan->note = note; + chan->inst_nr = patterns[pattern_nr][row][1]; + inst = &instruments[chan->inst_nr]; + chan->pos = inst->wave_table_pos; + if(inst->pulse_width) chan->pulse_width = inst->pulse_width; + } + } + } + } + } + if(++sample == tick_length) { + sample = 0; + if(++tick == row_length) { + tick = 0; + if(++row == pattern_length) { + row = 0; + if(++seq == tune_length) { + seq = 0; + } + } + } + } + + + uint16_t output = 0; + for(int i = 0; i < channel_count; i++) { + synth_channel_t* chan = &channels[i]; + const synth_instrument_t* inst = &instruments[chan->inst_nr]; + + chan->phase += freq_table[(uint8_t)(chan->note + wave_table[chan->pos][0])]; + + uint8_t amp; + switch(wave_table[chan->pos][1]) { + case WAVE_PULSE: + amp = -(chan->phase < chan->pulse_width); + break; + + case WAVE_SAW: + amp = (chan->phase >> 8); + break; + + case WAVE_NOISE: // shitty noise + chan->phase = (chan->phase >> 1) ^ (-(chan->phase & 1) & 0xb400); + amp = (chan->phase >> 8); + break; + + default: + amp = 0; + break; + } + + output += ((amp & 0xff) * chan->level) >> 8; + } + + return output; +} + diff --git a/synth/synth.h b/synth/synth.h deleted file mode 120000 index c1ea587..0000000 --- a/synth/synth.h +++ /dev/null @@ -1 +0,0 @@ -../firmware/synth.h \ No newline at end of file diff --git a/synth/synth.h b/synth/synth.h new file mode 100644 index 0000000..b094fb3 --- /dev/null +++ b/synth/synth.h @@ -0,0 +1,43 @@ +enum { WAVE_OFF, WAVE_PULSE, WAVE_SAW, WAVE_NOISE }; + +typedef struct { + uint8_t note; + uint8_t inst_nr; + uint8_t pos; + + uint16_t phase; + uint16_t pulse_width; + + uint8_t level; // envelop level + +} synth_channel_t; + + +typedef struct { + uint16_t pulse_width; + uint8_t pulse_sweep; + uint8_t decay; + uint8_t wave_table_pos; +} synth_instrument_t; + + +uint16_t synth_mix(void); + +enum { + channel_count = 3, + tick_length = 400, + row_length = 8, + pattern_length = 16 +}; + + +extern synth_instrument_t instruments[256]; +extern uint8_t wave_table[256][2]; +extern uint8_t patterns[256][pattern_length][2]; +extern uint8_t pattern_table[256][channel_count]; +extern uint8_t tune_length; + +extern int16_t sample; +extern int8_t tick; +extern int8_t row; +extern int8_t seq;