PlayMidiTones example modified to work with ramping.dds
/******************************************************************/ | /******************************************************************/ | ||||
// 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) | void AudioSynthWaveform::update(void) | ||||
{ | { | ||||
int32_t val1, val2, val3; | int32_t val1, val2, val3; | ||||
//Serial.println("AudioSynthWaveform::update"); | //Serial.println("AudioSynthWaveform::update"); | ||||
if (magnitude > 0 && (block = allocate()) != NULL) { | |||||
if (((magnitude > 0) || ramp_down) && (block = allocate()) != NULL) { | |||||
ph = phase; | ph = phase; | ||||
inc = phase_increment; | inc = phase_increment; | ||||
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) { | ||||
val2 *= scale; | val2 *= scale; | ||||
val1 *= 0xFFFF - scale; | val1 *= 0xFFFF - scale; | ||||
val3 = (val1 + val2) >> 16; | 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(block->data[i]); | ||||
//Serial.print(", "); | //Serial.print(", "); | ||||
//if ((i % 12) == 11) Serial.println(); | //if ((i % 12) == 11) Serial.println(); |
{ | { | ||||
public: | public: | ||||
AudioSynthWaveform(const int16_t *waveform) | 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) { | void frequency(float freq) { | ||||
if (freq > AUDIO_SAMPLE_RATE_EXACT / 2 || freq < 0.0) return; | if (freq > AUDIO_SAMPLE_RATE_EXACT / 2 || freq < 0.0) return; | ||||
phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f; | phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f; | ||||
void amplitude(float n) { // 0 to 1.0 | void amplitude(float n) { // 0 to 1.0 | ||||
if (n < 0) n = 0; | if (n < 0) n = 0; | ||||
else if (n > 1.0) n = 1.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; | magnitude = n * 32767.0; | ||||
} | } | ||||
virtual void update(void); | virtual void update(void); | ||||
void set_ramp_length(uint16_t r_length); | |||||
private: | private: | ||||
const int16_t *wavetable; | const int16_t *wavetable; | ||||
uint16_t magnitude; | uint16_t magnitude; | ||||
uint16_t last_magnitude; | |||||
uint32_t phase; | uint32_t phase; | ||||
uint32_t phase_increment; | uint32_t phase_increment; | ||||
uint32_t ramp_down; | |||||
uint32_t ramp_up; | |||||
uint32_t ramp_mag; | |||||
uint16_t ramp_length; | |||||
}; | }; | ||||
#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); | |||||
} |
// 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[]; |
#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; | |||||
} | |||||
} |
// Playtune bytestream for file "william_tell_overture.mid" created by MIDITONES V1.6 on Sat Oct 26 02:48:13 2013 | // 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 | // command line: ./miditones -t10 william_tell_overture | ||||
#include "Arduino.h" | #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, | 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, | 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, | 0x92,72, 0x93,72, 0x94,72, 0,41, 0x80, 0x81, 0x82, 0x83, 0x84, 0,58, 0x90,72, 0x91,72, 0x92,72, |