#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <LiquidCrystal.h>
// Generate audio samples using AudioSynthWaveform, use
// AudioAnalyzeFFT256 to generate a spectrum and output
// it to the LCD or the serial monitor
/*
2a
Typical output (where the frequency of 1723Hz was chosen because it
falls in the centre of a bin):
tone_freq = 1723, sine, amplitude = 0.0500, FFT = 16, Hanning
...
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 820, 3, 0, 0, 0, 0, 0, 0, 0, ....
(all the rest to the right are zero)
...
*/
// 1 = Display the spectrum on the LCD
// 0 = Print the spectrum on the Serial Monitor
const int LCD_DISPLAY = 0;
// Number of Spectra to average for one output of FFT
// and name of window to use (or NULL for the default)
int num_avg = 16;
const int16_t *window = NULL;
AudioAnalyzeFFT256 myFFT(num_avg,window);
// Allows the program to print info about the window being used
struct map_window_name {
const char *name;
const int16_t *array;
} map_win_name[] = {
// The first entry MUST be the default
{ "Hanning", AudioWindowHanning256},
{ "Bartlett", AudioWindowBartlett256},
{ "Blackman", AudioWindowBlackman256},
{ "Flattop", AudioWindowFlattop256},
{ "BlackmanHarris", AudioWindowBlackmanHarris256},
{ "Nuttall", AudioWindowNuttall256},
{ "BlackmanNuttall", AudioWindowBlackmanNuttall256},
{ "Welch", AudioWindowWelch256},
{ "Hamming", AudioWindowHamming256},
{ "Cosine", AudioWindowCosine256},
{ "Tukey", AudioWindowTukey256},
{ "Unknown", NULL},
};
// Map a window array address into its name
const char *window_name(const int16_t *ar)
{
int i;
if(ar == NULL)return(map_win_name[0].name);
for(i=0;map_win_name[i].array != NULL;i++) {
if(ar == map_win_name[i].array)return(map_win_name[i].name);
}
return(map_win_name[i].name);
}
// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
// Type of tone. SINE, SAWTOOTH, SQUARE or TRIANGLE
short type = TONE_TYPE_SINE;
#define TONE_FREQ 1723
#define TONE_LENGTH_MS 2000
#define SILENCE_LENGTH_MS 200
// Number of tones to send
int tone_count = 3;
// state of tone generator
int tone_state = 0;
// tone amplitude
float t_amp = 0.05;
char *wave_names[4] = {
"sine",
"sawtooth",
"square",
"triangle"
};
AudioSynthWaveform myEffect;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out
// Send the synthesized tone to the FFT and to the left and
// right audio output channels
AudioConnection c1(myEffect, 0, audioOutput, 0);
AudioConnection c2(myEffect, 0, myFFT, 0);
AudioConnection c3(myEffect, 0, audioOutput, 1);
// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;
// Use the LiquidCrystal library to display the spectrum
// Define 8 special characters for the bar graph display
LiquidCrystal lcd( 2, 3, 4, 5, 6, 7, 8);
byte bar1[8] = {
0,0,0,0,0,0,0,255};
byte bar2[8] = {
0,0,0,0,0,0,255,255};
byte bar3[8] = {
0,0,0,0,0,255,255,255};
byte bar4[8] = {
0,0,0,0,255,255,255,255};
byte bar5[8] = {
0,0,0,255,255,255,255,255};
byte bar6[8] = {
0,0,255,255,255,255,255,255};
byte bar7[8] = {
0,255,255,255,255,255,255,255};
byte bar8[8] = {
255,255,255,255,255,255,255,255};
// Index of first bin to be displayed on the LCD
int start_index = 0;
// character blocks which display the spectrum
char blocks[2][17];
void setup() {
Serial.begin(115200);
while(!Serial);
delay(2000);
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);
// Enable the audio shield and set the output volume.
audioShield.enable();
// audioShield.inputSelect(myInput);
audioShield.volume(60);
// I want output on the line out too
audioShield.unmuteLineout();
// audioShield.muteHeadphone();
blocks[0][16] = 0;
blocks[1][16] = 0;
lcd.begin(16, 2);
lcd.print("Audio Spectrum");
delay(2000);
lcd.createChar(0, bar1);
lcd.createChar(1, bar2);
lcd.createChar(2, bar3);
lcd.createChar(3, bar4);
lcd.createChar(4, bar5);
lcd.createChar(5, bar6);
lcd.createChar(6, bar7);
lcd.createChar(7, bar8);
myEffect.set_ramp_length(144);
// Start the tone generator with zero amplitude
myEffect.begin(t_amp,TONE_FREQ,type);
Serial.print("tone_freq = ");
Serial.print(TONE_FREQ);
Serial.print(", ");
Serial.print(wave_names[type]);
Serial.print(", amplitude = ");
Serial.print(t_amp,4);
Serial.print(", FFT = ");
Serial.print(num_avg);
Serial.print(", ");
Serial.println(window_name(window));
// Only need one tone when printing the results to
// the serial monitor
if(!LCD_DISPLAY)tone_count = 1;
}
// Map an amplitde to a special character
char cmap(int i)
{
if(i == 0)return(' ');
return(i-1);
}
// buffer and index for input from serial monitor
char tmp[128];
int idx = 0;
unsigned long last_time;
void loop() {
int log_mag;
int op;
char *p;
if(tone_count) {
switch(tone_state) {
case 0: // Start the tone generator and timer
myEffect.amplitude(t_amp);
last_time = millis();
// Wait for timer to expire
tone_state = 1;
break;
case 1: // Wait for timer to expire
if(millis() - last_time < TONE_LENGTH_MS)break;
myEffect.amplitude(0);
// Now "send" silence
tone_state = 2;
last_time = millis();
break;
case 2:
if(millis() - last_time < SILENCE_LENGTH_MS)break;
// count this tone and then reset for next one
tone_count--;
tone_state = 0;
break;
}
}
if(LCD_DISPLAY) {
if(start_index < 0)start_index = 0;
if(start_index > 112)start_index = 112;
if (myFFT.available()) {
for(int i=0;i < 16;i++) {
op = myFFT.output[i+start_index];
if(op < 0)op = 0;
log_mag = 0;
while(op) {
log_mag++;
op >>= 1;
}
//sprintf(tmp,"%4d (%2d),",op,log_mag);
//Serial.print(tmp);
// log_mag = log(myFFT.output[i])/log(1.4);
if(log_mag > 8) {
blocks[1][i] = 7;
if(log_mag >= 16)blocks[0][i] = 7;
else blocks[0][i] = cmap(log_mag - 8);
}
else {
blocks[0][i] = ' ';
blocks[1][i] = cmap(log_mag);
}
}
//Serial.println("");
lcd.setCursor(0,0);
lcd.write(blocks[0],16);
lcd.setCursor(0,1);
lcd.write(blocks[1],16);
}
}
else {
if(tone_count) {
if (myFFT.available()) {
for(int i=0;i < 128;i++) {
Serial.print(myFFT.output[i]);
Serial.print(", ");
}
Serial.println("");
}
}
}
while(Serial.available() > 0) {
char c = Serial.read();
if((c != '\n') && (c != '\r')) {
tmp[idx++] = c;
if(idx >= 127) {
Serial.println("Error: line too long");
idx = 0;
break;
}
continue;
}
tmp[idx] = 0;
if(idx == 0)continue;
idx = 0;
c = tmp[0];
p = &tmp[1];
while(*p && *p == ' ')p++;
if(*p == 0) {
idx = 0;
continue;
}
switch(tolower(c)) {
// By default the LCD display shows the first 16 bins.
// Typing the command 's N' where N is a number from
// 0 to 119 will display the 16 bins starting at index N
case 's':
start_index = atoi(p);
Serial.println(start_index);
break;
}
}
// Volume control
float vol = analogRead(15);
vol = vol / 10.24;
audioShield.volume(vol);
}