IDE 1.5.7 and 1.5.8 kill my wavetable synth. Help!

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. http://youtu.be/I7995CP7y1g

Here are download links to the test sketches: http://groovesizer.com/wp-content/uploads/2014/10/TB2_BUGTEST_WITH_LCD.zip http://groovesizer.com/wp-content/uploads/2014/10/TB2_BUGTEST_NO_LCD.zip

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).

Your insight would be greatly appreciated!

Have you looked at the changelog in revisions.txt?

You may be hitting a timing issue with some other interrupt handler, I'd look there first perhaps.

BTW use int not byte for-loop variables on the Due, its a 32 bit processor, int will be faster than byte.

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
  • Updates/Fix to various examples
  • Added Wire.setClock(…) method (Kristian Sloth Lauszus)

The following changes are included also in the (not yet released) Arduino IDE 1.0.6:

[core]

  • avr: Improved USB-CDC write speed (Justin Rajewski)
  • avr: Improved USB-CDC read code (Paul Brook)
  • avr: Fixed race condition in USB-CDC transmit (Paul Brook)
  • Fixed wrong NULL pointer handling in Stream class (Amulya Kumar Sahoo)
  • Added initVariant() hook to allow 3rd party variant-specific initialization

[ide]

  • Fix toolchain command line to compile assembler files (Jimmy Hedman)
  • If two libraries have the same header file use the lib with the same folder name (Paul Stoffregen)

[libraries]

  • Robot_Control: removed duplicated SPI and Wire (Xun Yang)
  • Robot_Control: fixed issue on motors being opposite (Xun Yang)
  • Robot_Control: updated turning algorithm (Xun Yang)
  • Esplora: added reading form Tinkerkit inputs
  • SoftwareSerial: Fix idle level when initializing with inverted logic (Jens-Christian Skibakk)

[firmware]

  • Wifishield: fixed paths on firmware upgrade scripts

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