This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mbed.h" | |
#include "music.h" | |
#include <math.h> | |
AnalogIn in(A0); | |
Ticker music_ticker; | |
#define knum_notes 8 | |
#define ksamples 71 | |
#define kfreq 71000 | |
AnalogOut out(PA_4); | |
void music_isr(); | |
DigitalOut led(LED1); | |
float frequencies[127]; | |
int periods[127]; | |
unsigned long song_data_counter = 0; | |
unsigned long cycles_counter = 0; | |
char notes_playing[knum_notes]; | |
int speedup = 1; | |
struct playing_note | |
{ | |
uint16_t cycles_to_go; | |
char midi_pitch; | |
}; | |
struct osc | |
{ | |
uint16_t phase; | |
uint16_t period; | |
uint16_t vol; | |
}; | |
osc oscs[knum_notes]; | |
playing_note current_notes[knum_notes]; | |
int main() | |
{ | |
led = 1; | |
for(int i = 0; i < 127; i++) | |
{ | |
frequencies[i] = 440*pow(2,(i - 69)/12.0f); | |
periods[i] = kfreq/frequencies[i]; | |
} | |
for(int i = 0; i < knum_notes; i++) | |
notes_playing[i] = 0; | |
music_ticker.attach(&music_isr, 1.0f/kfreq); | |
led = 0; | |
} | |
int max(int a, int b) | |
{ | |
if(a>b) return a; | |
return b; | |
} | |
char remap_midi(char in) | |
{ | |
while(in > 95) in-=12; | |
while(in < 40) in+=12; | |
return in; | |
} | |
void handle_notes(); | |
void music_isr() | |
{ | |
handle_notes(); | |
int now = speedup*cycles_counter/ksamples; | |
cycles_counter++; | |
const int* note_data = &(songdata[song_data_counter*4]); | |
int milliseconds = note_data[0]; | |
if(milliseconds < 0 ) | |
{ | |
song_data_counter++; | |
return; | |
} | |
if(milliseconds > now ) | |
return; | |
for(int i = 0; i < knum_notes; i++) | |
{ | |
if(!notes_playing[i]) | |
{ | |
notes_playing[i] = 1; | |
current_notes[i].cycles_to_go = max(note_data[1]*ksamples/speedup,6500); | |
current_notes[i].midi_pitch = remap_midi(note_data[2]); | |
song_data_counter++; | |
return; | |
} | |
} | |
} | |
void handle_notes() | |
{ | |
int num_ons = 0; | |
for(int i = 0; i < knum_notes; i++) | |
{ | |
if(notes_playing[i]) | |
{ | |
current_notes[i].cycles_to_go--; | |
if(current_notes[i].cycles_to_go == 0) | |
{ | |
notes_playing[i] = false; | |
continue; | |
} | |
oscs[i].period = periods[current_notes[i].midi_pitch]; | |
} | |
} | |
for(int i = 0; i < knum_notes; i++) | |
{ | |
if(notes_playing[i]) | |
{ | |
oscs[i].phase++; | |
if(oscs[i].phase > kfreq) oscs[i].phase = 0; | |
if(oscs[i].phase > (oscs[i].period/2)) | |
num_ons++; | |
if(oscs[i].phase > (oscs[i].period)) | |
oscs[i].phase = 0; | |
} | |
out.write_u16(8000*num_ons); | |
} | |
} |
Eventually, I plan to add more sounds and effects other than "square wave with 50% duty cycle", but for now, that's the only choice. The plan is to make each instrument kind of like a script. Each instrument would have an array of function pointers and arguments which get executed in order. Functions could do things like "delay 20 instrument cycles", "set output to triangle wave", "increase pitch by major third", "do a vibrato effect", or even "move function pointer array pointer back n steps".
I plugged the DAC into my computer's line in port, and recorded this:
https://www.youtube.com/watch?v=bGQ_Zj2jo8s
No comments:
Post a Comment