Browse Source

AudioSynthWaveform updated to add tone ramping when a tone starts and ends

PlayMidiTones example modified to work with ramping.
dds
Pete (El Supremo) 10 years ago
parent
commit
a27b78fc24
6 changed files with 241 additions and 115 deletions
  1. +45
    -3
      Audio.cpp
  2. +21
    -1
      Audio.h
  3. +0
    -110
      examples/PlayMidiTones/MidiTones.ino
  4. +29
    -0
      examples/PlayMidiTones/PlayMidiTones.h
  5. +145
    -0
      examples/PlayMidiTones/PlayMidiTones.ino
  6. +1
    -1
      examples/PlayMidiTones/william_tell_overture.c

+ 45
- 3
Audio.cpp View File





/******************************************************************/ /******************************************************************/

// 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();

+ 21
- 1
Audio.h View File

{ {
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;
}; };





+ 0
- 110
examples/PlayMidiTones/MidiTones.ino View File

#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);
}

+ 29
- 0
examples/PlayMidiTones/PlayMidiTones.h View File


// 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[];

+ 145
- 0
examples/PlayMidiTones/PlayMidiTones.ino View File

#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
- 1
examples/PlayMidiTones/william_tell_overture.c View File

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

Loading…
Cancel
Save