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, |