I'm working on a synth shield for the Arduino DUE. Everything was going well until I updated the IDE from 1.5.6-R2 to 1.5.8. My sketch compiles and uploads fine, but it sounds like it's gone through a shredder. I tested with 1.5.7 as well and experience the same problem. I've also tested on Win7 64 and Win7 32 - same result. If I go back to 1.5.6-R2 everything is fine.
I thought it might be related to one of the external libraries I'm using (DueTimer, MIDI, SdFat), so I created a paired down version with no external libraries (TB2 Bugtest with LCD). Problems persist. Removing the final library (LiquidCrystal.h) improves matters, but it's still broken.
Please see this video that illustrates the problem. - YouTube
Hardware wise, these sketches require 5 x 10k pots on A0 - A4. For audio, I'm simply using 2 x 500k resistors in series between the DAC pins and the audio output jack (one for each of the DACs).
Hey Mark,
No, I'd just looked at the release notes for 1.5.7 and 1.5.8 and could see no likely culprits there. I'll have a look in revisions.txt in case there's further information there.
Great, thanks for the suggestion - I'll replace byte with int in my for-loops.
For a sanity check I tried the same thing with my second DUE board - same problem: works with 1.5.6-R2, but not with 1.5.7.
The revisions.txt is the same as the release notes at http://arduino.cc/en/Main/ReleaseNotes
I can't see a likely culprit here:
ARDUINO 1.5.7 BETA - 2014.07.07
[core]
Upgraded AVR toolchain: gcc 4.8.1, avr-libc 1.8.0
Upgraded ARM toolchain: gcc 4.8.3-2014q1
Upgraded avrdude to version 6.0.1
ARM gcc doesn't require ia32-libs anymore on 64 bits linux systems
avr: fixed typo in SerialEvent3 handling (Matthijs Kooijman)
avr: HardwareSerial support for different size of TX and RX buffer sizes (Jan Baeyens)
avr: HardwareSerial support for buffer sizes bigger than 256 bytes (Jan Baeyens)
sam: Added configuration (parity, data bits, stop bits) to Serial1/2/3 of Arduino Due (bluesign2k)
Removed a lot of compiler warnings from Arduino core
avr: Fix EXTERNAL_NUM_INTERRUPTS for atmega128rfa1 and atmega256rfr2 (Matthijs Kooijman)
sam: Fix to Wire::endTransmisson() return value (bluesign2k)
sam: Fix to Wire usage of TWI status register (bluesign2k)
avr: Fixed PROGMEM statements to be compatible with newer avr gcc (Scott Howard)
[ide]
Moved to appbundler for building releases for MacOSX. (Haavar Valeur)
This should remove dependency from Java 1.6 on recent MacOSX.
Added support for '-' and '.' in filenames (Georg von Zengen)
(re)Added 'arduino_debug.exe' in Windows build for debugging purposes
Magic baudrate is no longer removed (it was a workaround for RXTX)
(for more info see github issues: #1203 and #995)
Allow overriding platform.txt using platform.local.txt (Matthijs Kooijman)
Explicitly define compiler.path in avr/platform.txt (Matthijs Kooijman)
Make the low available memory message a warning (Matt Robinson)
Proceed with upload even if port can't be found (David Mellis)
Added support for ArduinoISP
Windows: added board detection on serial port menu
[libraries]
Updated SpaceBrew library
Fixed HttpClient::running() function
Fixed HttpClient::ready() function (Manuel Rabade)
Added HttpClient::noCheckSSL() method
Improved speed of YunSerialTerminal
Fixed CRC of shutdown command on YunSerialTerminal example
Maybe try deleting the toolchain and bring the old one into 1.5.8? At least that would tell you if the issue is somewhere in the code you can see, or related to something in the new version of gcc/binutils/newlib.
Thanks so much for the suggestion, Paul. I found the problem. It's related to the way I adding up the current samples for my different voices (4 per oscillator) in the audio interrupt handler. What I had was
for (byte i = 0; i < 4; i++)
{
sampleOsc1 += (ulOutput[i] >> 2);
}
for (byte i = 0; i < 4; i++)
{
sampleOsc2 += (ulOutput[i + 4] >> 2);
}
Essentially I was adding a voice and dividing the sum by two (>> 2) before adding the next voice - come to think of it, that means the first voice weighs a lot more than the last. A better way to do it would be to add all the voices and divide by the number of voices, like so:
for (byte i = 0; i < 4; i++)
{
sampleOsc1 += ulOutput[i];
}
sampleOsc1 = sampleOsc1 / 4;
for (byte i = 0; i < 4; i++)
{
sampleOsc2 += ulOutput[i + 4];
}
sampleOsc2 = sampleOsc2 / 4;
I'm still not sure why the first method functioned properly under 1.5.6-R2, but not under 1.5.7. I'm very relieved that I can continue working though!
By the way for those who are interested, here's an example of mixing many voices adapted from DuaneB's Arduno Due DDS example
No pots required, just a 500 Ohm resistor between DAC0 and a powered speaker.
You can experiment with the number of voices in the line:
#define voices 8 // select how many voices
Duane's original has been super helpful!
// RCArduino DDS Sinewave for Arduino Due
// RCArduino DDS Sinewave by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on a work at rcarduino.blogspot.com.
// Adapted for many voices by MoShang http://groovesizer.com
// For helpful background information on Arduino Due Timer Configuration, refer to the following link
// thanks to Sebastian Vik
// http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20
// For background information on the DDS Technique see
// http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/
// For audio sketches making extensive use of DDS Techniques, search the RCArduino Blog
// for the tags Audio or synth
// These are the clock frequencies available to the timers /2,/8,/32,/128
// 84Mhz/2 = 42.000 MHz
// 84Mhz/8 = 10.500 MHz
// 84Mhz/32 = 2.625 MHz
// 84Mhz/128 = 656.250 KHz
//
// 44.1Khz = CD Sample Rate
// Lets aim for as close to the CD Sample Rate as we can get -
//
// 42Mhz/44.1Khz = 952.38
// 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample
// 2.625Hmz/44.1Khz = 59.5
// 656Khz/44.1Khz = 14.88
#define voices 8 // select how many voices
// 84Mhz/44.1Khz = 1904 instructions per tick
// the phase accumulator points to the current sample in our wavetable
uint32_t ulPhaseAccumulator[voices];
// the phase increment controls the rate at which we move through the wave table
// higher values = higher frequencies
volatile uint32_t ulPhaseIncrement[voices]; // 32 bit phase increment, see below
uint32_t ulOutput[voices];
// full waveform = 0 to SAMPLES_PER_CYCLE
// Phase Increment for 1 Hz =(SAMPLES_PER_CYCLE_FIXEDPOINT/SAMPLE_RATE) = 1Hz
// Phase Increment for frequency F = (SAMPLES_PER_CYCLE/SAMPLE_RATE)*F
#define SAMPLE_RATE 44100.0
#define SAMPLES_PER_CYCLE 600
#define SAMPLES_PER_CYCLE_FIXEDPOINT (SAMPLES_PER_CYCLE<<20)
#define TICKS_PER_CYCLE (float)((float)SAMPLES_PER_CYCLE_FIXEDPOINT/(float)SAMPLE_RATE)
// to represent 600 we need 10 bits
// Our fixed point format will be 10P22 = 32 bits
// We have 521K flash and 96K ram to play with
// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate
#define MIDI_NOTES 128
uint32_t nMidiPhaseIncrement[MIDI_NOTES];
int pot0 = 0;
boolean waveShape = 0; // 0 for sine, and 1 for square
// fill the note table with the phase increment values we require to generate the note
void createNoteTable(float fSampleRate)
{
for (uint32_t unMidiNote = 0; unMidiNote < MIDI_NOTES; unMidiNote++)
{
// Correct calculation for frequency
float fFrequency = ((pow(2.0, (unMidiNote - 69.0) / 12.0)) * 440.0);
nMidiPhaseIncrement[unMidiNote] = fFrequency * TICKS_PER_CYCLE;
}
}
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define WAVE_SAMPLES 600
// default int is 32 bit, in most cases its best to use uint32_t but for large arrays its better to use smaller
// data types if possible, here we are storing 12 bit samples in 16 bit ints
uint16_t nSineTable[WAVE_SAMPLES];
uint16_t nSquareTable[WAVE_SAMPLES];
// create the individual samples for our sinewave table
void createSineTable()
{
for (uint32_t nIndex = 0; nIndex < WAVE_SAMPLES; nIndex++)
{
// normalised to 12 bit range 0-4095
nSineTable[nIndex] = (uint16_t) (((1 + sin(((2.0 * PI) / WAVE_SAMPLES) * nIndex)) * 4095.0) / 2);
}
}
void createSquareTable()
{
for (uint32_t nIndex = 0; nIndex < WAVE_SAMPLES; nIndex++)
{
if (nIndex < (WAVE_SAMPLES / 2))
nSquareTable[nIndex] = 0;
else
nSquareTable[nIndex] = 4095;
}
}
void setup()
{
createNoteTable(SAMPLE_RATE);
createSineTable();
createSquareTable();
/* turn on the timer clock in the power management controller */
pmc_set_writeprotect(false);
pmc_enable_periph_clk(ID_TC4);
/* we want wavesel 01 with RC */
TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
TC_Start(TC1, 1);
// enable timer interrupts on the timer
TC1->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;
TC1->TC_CHANNEL[1].TC_IDR = ~TC_IER_CPCS;
/* Enable the interrupt in the nested vector interrupt controller */
/* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */
NVIC_EnableIRQ(TC4_IRQn);
// this is a cheat - enable the DAC
analogWrite(DAC0, 0);
}
void loop() // loop adapted so no potentiometer is required (MoShang)
{
static int pitch = 45; // range 0 to 128
static unsigned long pitchTimer = 0;
pot0 = analogRead(0);
if ((millis() - pitchTimer) > 150)
{
if (pitch < 85)
pitch++;
else
{
pitch = 45;
waveShape = !waveShape; // toggle between the two waveforms
}
pitchTimer = millis();
}
for (int i = 0; i < voices; i++)
{
ulPhaseIncrement[i] = nMidiPhaseIncrement[pitch + (i * 2)];
}
}
void TC4_Handler()
{
// We need to get the status to clear it and allow the interrupt to fire again
TC_GetStatus(TC1, 1);
for (int i = 0; i < voices; i++)
{
ulPhaseAccumulator[i] += ulPhaseIncrement[i]; // 32 bit phase increment, see below
// if the phase accumulator over flows - we have been through one cycle at the current pitch,
// now we need to reset the grains ready for our next cycle
if (ulPhaseAccumulator[i] > SAMPLES_PER_CYCLE_FIXEDPOINT)
{
// DB 02/Jan/2012 - carry the remainder of the phase accumulator
ulPhaseAccumulator[i] -= SAMPLES_PER_CYCLE_FIXEDPOINT;
}
}
uint32_t sampleOut = 0;
// get the current sample
if (waveShape == 1)
{
for (int i = 0; i < voices; i++)
sampleOut += nSquareTable[ulPhaseAccumulator[i] >> 20];
}
else
{
for (int i = 0; i < voices; i++)
sampleOut += nSineTable[ulPhaseAccumulator[i] >> 20];
}
sampleOut = sampleOut / voices;
// we cheated and user analogWrite to enable the dac, but here we want to be fast so
// write directly
dacc_write_conversion_data(DACC_INTERFACE, sampleOut);
}