Using Teensy 3.6 two DACs with Audio library

Status
Not open for further replies.

terdjman

Member
Hello everybody,

I'm working on a project that requires stereo output, that's why I bought a 3.6.
Is there only one DAC accessible through the audio library ?
How can this be modified ?
I'm definitely not an expert, I had a quick look at the libraries files and prefered asking here :rolleyes:

Cheers !
 
Hello everybody,

I'm working on a project that requires stereo output, that's why I bought a 3.6.
Is there only one DAC accessible through the audio library ?
How can this be modified ?
I'm definitely not an expert, I had a quick look at the libraries files and prefered asking here :rolleyes:

Cheers !

I believe Paul has said he has plans to enhance the audio library to support the second DAC. I don't know how important this is compared to the other things on his plate. Though if somebody were to step up and pitch in, it might be helpful.

Briefly looking at the library, the DAC support is in just 2 files: output_dac.h and output_dac.cpp in the teensy/hardware/teensy/avr/libraries/Audio directory. I could imagine two ways to add the second DAC:
  • Clone the files to add second class (AudioOutputAnalog2?) that does the same thing as the original files, but for the second DAC;
  • Add secondary a AudioOutputAnalog constructor and begin function that takes an integer argument that says whether you are dealing with the first or second DAC.

The second is simpler if the two DAC's are mostly the same.

Obviously after the code is done, you have to add options to the GUI to create the second DAC.

Assuming the second method is used, you would do something like:

Code:
AudioSynthWaveformSine   sine1;
AudioSynthWaveformSine   sine2;
AudioOutputAnalog               dac1(0);
AudioOutputAnalog               dac2(1);
AudioConnection                  patchCord1(sine1, dac1);
AudioConnection                  patchCord1(sine2, dac2);

void setup (void) {
  AudioMemory(12);
  sine1.amplitude(1.0);
  sine1.frequency(100);
  sine2.amplitude(2.0);
  sine2.frequency(200);
}

void loop (void) {
}
[/code]
 
Hi MichaelMeissner,

I checked these two files already... I don't get anything. Any code with capitals and underscores is frightening me too much :(
And I don't get why there are three AudioOutputAnalog already.
 
Last edited:
Hi MichaelMeissner,

I checked these two files already... I don't get anything. Any code with capitals and underscores is frightening me too much :(
Then unfortunately, you will have to wait for somebody who can understand the datasheets, and has the time and desire to write code to add the second DAC support.

And I don't get why there are three AudioOutputAnalog already.

I'm not sure I understand this comment. In the Audio library there is only one AudioOutputAnalog device, and that is the DAC on the Teensy 3.1/3.2 and DAC0 on the Teensy 3.5/3.6. There are other Audio Output devices, such as the speakers and/or I2S that you can plug into.

If you mean why is there output_dac.h and output_dac.cpp, that is a C/C++ convention. The .h file is the header that defines the interface, and it is included in any sketch that wants to use that interface. The .cpp file is the implementation of the interface, and it provides the code to actually write to the DAC.
 
Last edited:
I'm not sure I understand this comment. In the Audio library there is only one AudioOutputAnalog device, and that is the DAC on the Teensy 3.1/3.2 and DAC0 on the Teensy 3.5/3.6. There are other Audio Output devices, such as the speakers and/or I2S that you can plug into.

I meant "void AudioOutputAnalog::begin(void)" inside output_dac.cpp
 
If it were me, I would probably try the first approach that Michael mentioned, it is probably easier.

And clone the two files.

In these files change all AudioOutputAnalog to AudioOutputAnalog2
Change all: DAC0_ to DAC1_
Change: SIM_SCGC2_DAC0 to SIM_SCGC2_DAC1
Change #if statement to only allow for T3.5/3.6 and remove Teensy LC code.
change which header file is included and #if statements.
Add a reference to output_dac2.h to Audio.h

I have done the above and at least one of the Audio examples still compiles, but I have not tried it... But if anyone wishes to take a look, I included the files in the below zip file.
Note: I have the current Beta2 of Teensyduino.

If anyone tries it and it works, can issue pull request or maybe Paul likes the 2nd approach...
 

Attachments

  • Audio.zip
    5.3 KB · Views: 150
If it were me, I would probably try the first approach that Michael mentioned, it is probably easier.

And clone the two files.
I tend to like the second alternative for two reasons.

1) In theory, it can simplify updates, in that you only have to modify 1 file, not 2. I've had a number of cases, where I had similar code, and I would edit one but not the other, or not do all of the transformations;

2) Having separate types gives you the Serial1, Serial2, Serial3 problem where you cannot take the address of one of the Serial types simply. You have to convert the pointer to underlying base type.

But, it isn't my call. Which ever Paul picks is fine.
 
Hi KurtE,

wow ! thanks a lot for the infos. I downloaded and replaced the files.
Everything compiles fine. But the problem stays the same. A gigantic VERY loud buzz on one channel, and nothing on the other.

Here is the very simple code I'm using to test it :
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include "SdFat.h"


AudioSynthWaveform       waveform0;
AudioSynthWaveform       waveform1;
AudioOutputAnalog        dac1;
AudioOutputAnalog        dac2;
AudioConnection          patchCord1(waveform0, dac1);
AudioConnection          patchCord2(waveform1, dac2);

void setup(){

  Serial.begin(57600);
  
  AudioMemory(16);
  analogReference(INTERNAL);
  
  waveform0.begin(0.2, 110, WAVEFORM_SINE);
  waveform1.begin(0.2, 120, WAVEFORM_SINE);

}

void loop(){
  
}
 
In your Example here, try changing: AudioOutputAnalog dac2;

to: AudioOutputAnalog2 dac2;
 
In your Example here, try changing: AudioOutputAnalog dac2;

to: AudioOutputAnalog2 dac2;

Thank you ! It is working ! The sound is pretty distorted though. Closer to a square than a sine wave. It tried with an increased AudioMemory and also adding a mixer with .1 gain, still the same distortion.
 
If it were me, I would then try just the dac2 channel and see how it sounds by itself.

It may be that there is another shared resource between the two units like the PDB0, which need to be worked out.
 
I believe Paul has said he has plans to enhance the audio library to support the second DAC. I don't know how important this is compared to the other things on his plate.

I seriously considered doing this as a last-minute addition before publishing 1.31-beta2. It's really not very difficult, but as the conversation below shows, the devil is in the details.

At this moment I'm doing documentation stuff (hopefully you can see a few web pages now have 3.5 & 3.6 info) and recovering from a cold. I'll be back to software in a couple days. DAC1 support and fixing the complex USB types for Windows 10 are my top priorities. My hope is to get a beta3 installer done by the end of this week, and finalize 1.31 (not beta) sometime next week.
 
I've added stereo DAC support to the audio library.

https://github.com/PaulStoffregen/Audio/commit/c74b111431efb94395d056a3ee08cec1cf202ef6

A single object supports both DACs. This is done to keep them perfectly in sync using a single DMA channel. There isn't any way to use only DAC1 without DAC0.

Here's the sketch I've been using for testing.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioPlaySdWav           playWav1;
AudioOutputAnalogStereo  audioOutput;
//AudioOutputAnalog        audioOutput;
AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);

void setup() {
  Serial.begin(9600);
  while (!Serial && millis() < 2500) ; // wait
  Serial.println("Stereo DAC test");

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(12);
  
  if (!(SD.begin(BUILTIN_SDCARD))) {
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

void playFile(const char *filename)
{
  Serial.print("Playing file: ");
  Serial.println(filename);

  // Start playing the file.  This sketch continues to
  // run while the file plays.
  playWav1.play(filename);

  // A brief delay for the library read WAV info
  delay(5);

  // Simply wait for the file to finish playing.
  while (playWav1.isPlaying()) {
  }
}


void loop() {
  playFile("SDTEST1.WAV");  // filenames are always uppercase 8.3 format
  delay(500);
  playFile("SDTEST2.WAV");
  delay(500);
  playFile("SDTEST3.WAV");
  delay(500);
  playFile("SDTEST4.WAV");
  delay(1500);
}
 
Hi Paul,

Looks good.

My version was working for either DAC0 or DAC1 but as soon as you defined both, they failed (garbage sound). Bright side was it was helping me learn some new stuff about the processor/libraries, like some of the interactions between DAC and PDB and DMA... May play a little longer, try to understand more. Like it sounded like in the PDB that there are two triggers for DAC0 and DAC1, but was not clear yet to me, if that would imply PDB0_CH0C1 would have different values or if it would be something like PDB0_CH1C1 ... But again yours is the better approach as the user probably wanted both for Stereo.

Now back to my normal interrupts :D
 
I believe the only problem was using the PDB to trigger 2 different DMA channels. To make that work, you'd need to have the PDB trigger 1 channel, then using the channel linking to have that channel trigger the other one. It could suffer from "strange" startup timing issues if the first channel starts running before the 2nd channel is fully set up.
 
I believe the only problem was using the PDB to trigger 2 different DMA channels. To make that work, you'd need to have the PDB trigger 1 channel, then using the channel linking to have that channel trigger the other one. It could suffer from "strange" startup timing issues if the first channel starts running before the 2nd channel is fully set up.

Yep - That makes sense as it shows the dma.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB); in both cases. As you said trying to get them both to work independent may be problematic. So I punt! ;)
 
What is the logic of 2 dac output?
Is it like the 2 channel line-out of the audio board so it can play 2 channel audio file?
Or it simply outputs the same signal on both dac 0&1?
 
Status
Not open for further replies.
Back
Top