PlayMidiTones example modified to work with ramping.dds
| @@ -139,7 +139,21 @@ void AudioAnalyzeFFT256::update(void) | |||
| /******************************************************************/ | |||
| // PAH - add ramp-up and ramp-down at the beginning and end of the wave | |||
| // | |||
| void AudioSynthWaveform::set_ramp_length(uint16_t r_length) | |||
| { | |||
| if(r_length < 0) { | |||
| ramp_length = 0; | |||
| return; | |||
| } | |||
| // Don't set the ramp length longer than about 4 milliseconds | |||
| if(r_length > 44*4) { | |||
| ramp_length = 44*4; | |||
| return; | |||
| } | |||
| ramp_length = r_length; | |||
| } | |||
| void AudioSynthWaveform::update(void) | |||
| { | |||
| @@ -148,7 +162,7 @@ void AudioSynthWaveform::update(void) | |||
| int32_t val1, val2, val3; | |||
| //Serial.println("AudioSynthWaveform::update"); | |||
| if (magnitude > 0 && (block = allocate()) != NULL) { | |||
| if (((magnitude > 0) || ramp_down) && (block = allocate()) != NULL) { | |||
| ph = phase; | |||
| inc = phase_increment; | |||
| for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | |||
| @@ -159,7 +173,35 @@ void AudioSynthWaveform::update(void) | |||
| val2 *= scale; | |||
| val1 *= 0xFFFF - scale; | |||
| val3 = (val1 + val2) >> 16; | |||
| block->data[i] = (val3 * magnitude) >> 15; | |||
| // The value of ramp_up is always initialized to RAMP_LENGTH and then is | |||
| // decremented each time through here until it reaches zero. | |||
| // The value of ramp_up is used to generate a Q15 fraction which varies | |||
| // from [0 - 1), and multiplies this by the current sample | |||
| if(ramp_up) { | |||
| // ramp up to the new magnitude | |||
| // ramp_mag is the Q15 representation of the fraction | |||
| // Since ramp_up can't be zero, this cannot generate +1 | |||
| ramp_mag = ((ramp_length-ramp_up)<<15)/ramp_length; | |||
| ramp_up--; | |||
| block->data[i] = (val3 * ((ramp_mag * magnitude)>>15)) >> 15; | |||
| } else if(ramp_down) { | |||
| // ramp down to zero from the last magnitude | |||
| // The value of ramp_down is always initialized to RAMP_LENGTH and then is | |||
| // decremented each time through here until it reaches zero. | |||
| // The value of ramp_down is used to generate a Q15 fraction which varies | |||
| // from (1 - 0], and multiplies this by the current sample | |||
| // avoid RAMP_LENGTH/RAMP_LENGTH because Q15 format | |||
| // cannot represent +1 | |||
| ramp_mag = ((ramp_down - 1)<<15)/ramp_length; | |||
| ramp_down--; | |||
| block->data[i] = (val3 * ((ramp_mag * last_magnitude)>>15)) >> 15; | |||
| } else { | |||
| block->data[i] = (val3 * magnitude) >> 15; | |||
| } | |||
| //Serial.print(block->data[i]); | |||
| //Serial.print(", "); | |||
| //if ((i % 12) == 11) Serial.println(); | |||
| @@ -73,7 +73,9 @@ class AudioSynthWaveform : public AudioStream | |||
| { | |||
| public: | |||
| AudioSynthWaveform(const int16_t *waveform) | |||
| : AudioStream(0, NULL), wavetable(waveform), magnitude(0), phase(0) { } | |||
| : AudioStream(0, NULL), wavetable(waveform), magnitude(0), phase(0) | |||
| , ramp_up(0), ramp_down(0), ramp_mag(0), ramp_length(0) | |||
| { } | |||
| void frequency(float freq) { | |||
| if (freq > AUDIO_SAMPLE_RATE_EXACT / 2 || freq < 0.0) return; | |||
| phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f; | |||
| @@ -81,14 +83,32 @@ public: | |||
| void amplitude(float n) { // 0 to 1.0 | |||
| if (n < 0) n = 0; | |||
| else if (n > 1.0) n = 1.0; | |||
| // Ramp code | |||
| if(magnitude && (n == 0)) { | |||
| ramp_down = ramp_length; | |||
| ramp_up = 0; | |||
| last_magnitude = magnitude; | |||
| } | |||
| else if((magnitude == 0) && n) { | |||
| ramp_up = ramp_length; | |||
| ramp_down = 0; | |||
| } | |||
| // set new magnitude | |||
| magnitude = n * 32767.0; | |||
| } | |||
| virtual void update(void); | |||
| void set_ramp_length(uint16_t r_length); | |||
| private: | |||
| const int16_t *wavetable; | |||
| uint16_t magnitude; | |||
| uint16_t last_magnitude; | |||
| uint32_t phase; | |||
| uint32_t phase_increment; | |||
| uint32_t ramp_down; | |||
| uint32_t ramp_up; | |||
| uint32_t ramp_mag; | |||
| uint16_t ramp_length; | |||
| }; | |||
| @@ -1,110 +0,0 @@ | |||
| #include <Audio.h> | |||
| #include <Wire.h> | |||
| //#include <WM8731.h> | |||
| #include <SD.h> | |||
| #include <SPI.h> | |||
| //AudioInputI2S adc; | |||
| //AudioInputAnalog ana(16); | |||
| AudioSynthWaveform mysine(AudioWaveformSine); | |||
| AudioSynthWaveform sine2(AudioWaveformSine); | |||
| //AudioOutputPWM myout; | |||
| //AudioPlaySDcardWAV wav; | |||
| //AudioPlaySDcardRAW wav; | |||
| //AudioMixer4 mix; | |||
| AudioOutputI2S dac; | |||
| //AudioControl_WM8731 codec; | |||
| AudioControlSGTL5000 codec; | |||
| AudioConnection c1(mysine, dac); | |||
| //AudioConnection c1(wav, dac); | |||
| //AudioConnection c2(wav, 1, dac, 1); | |||
| int volume = 0; | |||
| void setup() { | |||
| Serial1.begin(115200); | |||
| Serial1.println("***************"); | |||
| while (!Serial) ; | |||
| delay(100); | |||
| codec.enable(); | |||
| //while(1); | |||
| delay(200); | |||
| Serial.println("Begin AudioTest"); | |||
| delay(50); | |||
| Serial1.print("B"); | |||
| // Audio connections require memory to work. For more | |||
| // detailed information, see the MemoryAndCpuUsage example | |||
| AudioMemory(15); | |||
| //mysine.connect(myout); | |||
| //mysine.connect(dac); | |||
| //mysine.connect(dac, 1, 0); | |||
| mysine.frequency(440); | |||
| //wav.connect(dac); | |||
| //wav.connect(dac, 1, 0); | |||
| //codec.inputLinein(); | |||
| codec.volume(40); | |||
| //adc.connect(dac); | |||
| //ana.connect(dac); | |||
| //ana.connect(dac, 1, 0); | |||
| /* | |||
| SPI.setMOSI(7); | |||
| SPI.setSCK(14); | |||
| if (SD.begin(10)) { | |||
| Serial.println("SD init ok"); | |||
| //wav.play("01_16M.WAV"); | |||
| } | |||
| */ | |||
| Serial.println("setup done"); | |||
| } | |||
| void loop() { | |||
| /* | |||
| Serial.print("cpu: "); | |||
| Serial.print(AudioProcessorUsage()); | |||
| Serial.print(", max: "); | |||
| Serial.print(AudioProcessorUsageMax()); | |||
| Serial.print(", memory: "); | |||
| Serial.print(AudioMemoryUsage()); | |||
| Serial.print(", max: "); | |||
| Serial.print(AudioMemoryUsageMax()); | |||
| Serial.println(""); | |||
| */ | |||
| //int n; | |||
| //n = analogRead(15); | |||
| //Serial.println(n); | |||
| //if (n != volume) { | |||
| //volume = n; | |||
| //codec.volume((float)n / 10.23); | |||
| //} | |||
| //n = analogRead(16) / 8; | |||
| //Serial.println(n); | |||
| //mysine.frequency(200 + n * 4); | |||
| //delay(5); | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| // Table of midi note frequencies * 2 | |||
| // They are times 2 for greater accuracy, yet still fits in a word. | |||
| // Generated from Excel by =ROUND(2*440/32*(2^((x-9)/12)),0) for 0<x<128 | |||
| // The lowest notes might not work, depending on the Arduino clock frequency | |||
| const unsigned int tune_frequencies2_PGM[128] = | |||
| { | |||
| 16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41, | |||
| 44,46,49,52,55,58,62,65,69,73,78,82,87,92,98,104,110, | |||
| 117,123,131,139,147,156,165,175,185,196,208,220,233, | |||
| 247,262,277,294,311,330,349,370,392,415,440,466,494, | |||
| 523,554,587,622,659,698,740,784,831,880,932,988,1047, | |||
| 1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976, | |||
| 2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729, | |||
| 3951,4186,4435,4699,4978,5274,5588,5920,6272,6645,7040, | |||
| 7459,7902,8372,8870,9397,9956,10548,11175,11840,12544, | |||
| 13290,14080,14917,15804,16744,17740,18795,19912,21096, | |||
| 22351,23680,25088 | |||
| }; | |||
| #define AMPLITUDE (0.4) | |||
| #define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */ | |||
| #define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */ | |||
| #define CMD_RESTART 0xe0 /* restart the score from the beginning */ | |||
| #define CMD_STOP 0xf0 /* stop playing */ | |||
| // User must supply this score array | |||
| extern unsigned char score[]; | |||
| @@ -0,0 +1,145 @@ | |||
| #include <Audio.h> | |||
| #include <Wire.h> | |||
| //#include <WM8731.h> | |||
| #include <SD.h> | |||
| #include <SPI.h> | |||
| #include "PlayMidiTones.h" | |||
| unsigned char *sp = score; | |||
| //AudioInputI2S adc; | |||
| //AudioInputAnalog ana(16); | |||
| AudioSynthWaveform sine0(AudioWaveformSine); | |||
| AudioSynthWaveform sine1(AudioWaveformTriangle); | |||
| AudioSynthWaveform sine2(AudioWaveformSquare); | |||
| AudioSynthWaveform sine3(AudioWaveformSawtooth); | |||
| AudioSynthWaveform sine4(AudioWaveformSine); | |||
| AudioSynthWaveform sine5(AudioWaveformTriangle); | |||
| AudioSynthWaveform sine6(AudioWaveformSquare); | |||
| AudioSynthWaveform sine7(AudioWaveformSawtooth); | |||
| AudioSynthWaveform *waves[8] = { | |||
| &sine0, | |||
| &sine1, | |||
| &sine2, | |||
| &sine3, | |||
| &sine4, | |||
| &sine5, | |||
| &sine6, | |||
| &sine7, | |||
| }; | |||
| AudioMixer4 mixer1; | |||
| AudioMixer4 mixer2; | |||
| AudioOutputI2S audioOut; | |||
| AudioConnection c0(sine0, 0, mixer1, 0); | |||
| AudioConnection c1(sine1, 0, mixer1, 1); | |||
| AudioConnection c2(sine2, 0, mixer1, 2); | |||
| AudioConnection c3(sine3, 0, mixer1, 3); | |||
| AudioConnection c4(sine4, 0, mixer2, 0); | |||
| AudioConnection c5(sine5, 0, mixer2, 1); | |||
| AudioConnection c6(sine6, 0, mixer2, 2); | |||
| AudioConnection c7(sine7, 0, mixer2, 3); | |||
| AudioConnection c11(mixer1, 0, audioOut, 0); | |||
| AudioConnection c12(mixer2, 0, audioOut, 1); | |||
| //AudioControl_WM8731 codec; | |||
| AudioControlSGTL5000 codec; | |||
| int volume = 50; | |||
| void setup() | |||
| { | |||
| Serial.begin(115200); | |||
| while (!Serial) ; | |||
| delay(3000); | |||
| // Audio connections require memory to work. For more | |||
| // detailed information, see the MemoryAndCpuUsage example | |||
| AudioMemory(8); | |||
| codec.enable(); | |||
| codec.volume(volume); | |||
| // I want output on the line out too | |||
| codec.unmuteLineout(); | |||
| // Comment out this code to hear what it sounds like | |||
| // when the tones aren't ramped. (or change 88 to 0) | |||
| // Set the ramp time for each wave | |||
| for(int i = 0; i < 8;i++) { | |||
| waves[i]->set_ramp_length(88); | |||
| } | |||
| delay(200); | |||
| Serial.println("Begin PlayMidiTones"); | |||
| delay(50); | |||
| Serial.println("setup done"); | |||
| AudioProcessorUsageMaxReset(); | |||
| AudioMemoryUsageMaxReset(); | |||
| } | |||
| unsigned long last_time = millis(); | |||
| void loop() | |||
| { | |||
| unsigned char c,opcode,chan; | |||
| unsigned long d_time; | |||
| if(0) { | |||
| // For PlayMidiTones this produces: | |||
| // Proc = 6 (12), Mem = 2 (8) | |||
| if(millis() - last_time >= 5000) { | |||
| Serial.print("Proc = "); | |||
| Serial.print(AudioProcessorUsage()); | |||
| Serial.print(" ("); | |||
| Serial.print(AudioProcessorUsageMax()); | |||
| Serial.print("), Mem = "); | |||
| Serial.print(AudioMemoryUsage()); | |||
| Serial.print(" ("); | |||
| Serial.print(AudioMemoryUsageMax()); | |||
| Serial.println(")"); | |||
| last_time = millis(); | |||
| } | |||
| } | |||
| // Volume control | |||
| int n = analogRead(15); | |||
| if (n != volume) { | |||
| volume = n; | |||
| codec.volume((float)n / 10.23); | |||
| } | |||
| c = *sp++; | |||
| opcode = c & 0xf0; | |||
| chan = c & 0x07; | |||
| if(c < 0x80) { | |||
| // Delay | |||
| d_time = (c << 8) | *sp++; | |||
| delay(d_time); | |||
| return; | |||
| } | |||
| if(*sp == CMD_STOP) { | |||
| Serial.println("DONE"); | |||
| while(1); | |||
| } | |||
| // It is a command | |||
| if(opcode == CMD_STOPNOTE) { | |||
| waves[chan]->amplitude(0); | |||
| return; | |||
| } | |||
| if(opcode == CMD_PLAYNOTE) { | |||
| waves[chan]->frequency(tune_frequencies2_PGM[*sp++]); | |||
| waves[chan]->amplitude(AMPLITUDE); | |||
| return; | |||
| } | |||
| if(opcode == CMD_RESTART) { | |||
| sp = score; | |||
| return; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| // Playtune bytestream for file "william_tell_overture.mid" created by MIDITONES V1.6 on Sat Oct 26 02:48:13 2013 | |||
| // command line: ./miditones -t10 william_tell_overture | |||
| #include "Arduino.h" | |||
| const byte PROGMEM score [] = { | |||
| const unsigned char score [] = { | |||
| 0x90,72, 0x91,72, 0x92,72, 0x93,72, 0x94,72, 1,169, 0x80, 0x81, 0x82, 0x83, 0x84, 0,183, 0x90,72, | |||
| 0x91,72, 0x92,72, 0x93,72, 0x94,72, 0,41, 0x80, 0x81, 0x82, 0x83, 0x84, 0,58, 0x90,72, 0x91,72, | |||
| 0x92,72, 0x93,72, 0x94,72, 0,41, 0x80, 0x81, 0x82, 0x83, 0x84, 0,58, 0x90,72, 0x91,72, 0x92,72, | |||