Connecting DAC directly to powered speakers. Result?

Status
Not open for further replies.

Projectitis

Well-known member
Hi all,

I have audio output from an xm mod player that I ported for T3 going to the DAC pins.
I'm waiting for some components to turn up so that I can complete my audio circuit. I don't even have suitable capacitors to connect between DAC and input, so I've connected them directly!
What would I expect to hear if I did this? This is what I get:
  • Audio playing and it's obviously the right song, but sound is extremely "crunchy"
  • Especially the low frequency sounds are hard to hear or not there
  • Constant high pitched hum in the background
Is this about what i should expect at this stage?

Cheers,
Peter
 
This is most probably the best way to kill the DACs... Since these are unipolar, there is always a DC offset with the audio signal. Connecting whatever without decoupling capacitors will in the best case give an ugly and distorted sound. In the worst case (depending on the input impedance of what you connect), the power dissipation in the DACs (due to the virtually shorted offset) will exceed the allowed amount and thus damage the DAC.

Good practice for digital audio (that's what I consider being the strict minimum requirement in the hobbyist world) is using a 18dB/oct Bessel filter with a Fc of 0.45*Fs (if no oversampling is done) to remove the sampling aliases (your high pitched distortion) and DC blocking capacitors.
 
Great info, thanks.

I've been trying to understand the datasheet for the TDA1308 stereo preamp I have on the way, and I can't find any mention of filtering.
So I'm guessing I still need a filter to remove sampling aliases even with this? From what I understand, Fc = cutoff freq, Fs = sampling frequency, which is 48kHz in my case, so Fc of 21.6kHz.
You've already said the capacitors are on the TDA1308 already, so I'm guessing these are the DC blocking capacitors.
 
As we discussed previously, the TDA1308 module which you ordered has the DC blocking capacitors. It does not have filtering since it is not specially designed for use with DAC outputs but for general use. Thus, it's up to you to add a filter if needed. It might be that the audio bandwidth of the module will be already somewhat limited, so that you'll probably see sufficient inherent or accidental filtering.

Until the TDA1308 module arrives, I'd try the following simple DC block and 6dB filter approach which is build with common components which every circuit builder has somewhere in the drawer [C1 = 1uF electrolytic, R1 = 10k, C2 = 1nF] :

Code:
DAC OUT --(+)C1-----R1--+--> AUDIO OUT TO ACTIVE SPEAKER
                        |
                       C2
                        |
AGND -------------------+--> GND
 
I’m not a circuit builder, as you have guessed ( :D ). I’m a coder, and interested in teensy mostly for the coding challenge, but even I have those components in the drawer! I’ll give it a try tomorrow. Thanks for the help.
 
At the beginning, more than 50 years ago, I was neither a coder nor a circuit builder. I was just there, cried, and peed in my pants. Every competence might be acquired during a human's life :)
 
If you are a coder......I assume you are using the audio library......pass the output via mixer to DAC and set the gain low 0.1 or 0.2.
It takes very little amplitude to hear the signals clearly......your problem is probably far too much volume causing clipping and all sorts of distortion.
Set dac.analogReference(INTERNAL) ......that will reduce the DC bias and signal out.
Use an amplified speaker to listen.....not an 8 ohm speaker direct......32 ohm headphones through a small cap and series resistor work but not recommended, but if amplitude is kept low it works.
Use GND for power grounds and AGND for DAC ground
This is a Spice simulation of how little amplitude of signal milliVolts will give a very loud output.......
 

Attachments

  • DAC Pin Output.JPG
    DAC Pin Output.JPG
    114.6 KB · Views: 170
Awesome info. Will give some of that a shot tomorrow. No, not using the audio library - I’ve ported an xm module player (old school Amiga tracker music) to teensy and am driving the DAC pins at 48kHz using an IntervalTimer. Lots of your tips apply though, so I’ll give them a shot.
 
OK......now we are actually finding out what you are trying to do.......??
You will be using something like ..... analogWrite(A14, value)......the value need to be no more than 12 bits........0 to 4096.......for max amplitude. If your audio data is say 16 bit signed you will need to convert it to 12 bit all positive....you probably need to add 30000 then divide by 16.....near enough ...... should give 0 to 4000.

See the example on the PJRC web pages for DAC 12 bit sinewave.......this is what they do ......... float val = sin(phase) * 2000.0 + 2050.0; .......... analogWrite(A14, (int)val);
multiply by 2000 and add 2000 to convert from -1, 0, +1, to 0 - 4000 approx
 
If I read well from the beginning of this thread, the digital part works already, as Projectitis stated, and his questions were only about the analog post-DAC audio processing...
 
yes, but the output should not be "crunchy".....best way would be to listen on an amplified speaker that is known to be working first before designing a new one. that way you would know the DAC output is OK to start with
 
Thanks guys - Theremingenieur is correct. I didn't come in to this blind :) The samples being generated are floats in the -1.0f <= s <= 1.0f range. On testing, they are easily below 0.20f almost all of the time. These are converted to 12 bit range (0.0f == 2048) and then clamped (0 <= s <= 4095).

The xm player already has built in amplification set to 0.25f (this is similar to the gain you mentioned, Teenfor3). If I reduce this too much more I lose the audio altogether, but as soon as I get audio out (the speakers are a portable set of powered/active speakers) the sound is crunchy, almost like every second sample is missing. I'm assuming that this is completely due to the hardware - but I will be building Theremingeneiur's suggested filter after work today.

I've also tested to ensure that the audio player is indeed running at 48kHz, and also that the ringbuffer remains full. In practice the T3.6 is so fast that only 1 sample pair is pushed to the ringbuffer about every 4 "update" loops. Much better than I was anticipating!

My original question was purely "I know I am doing this wrong, but is what I'm hearing correct for what I am doing wrong?" :) It's good to be able to understand when things are going wrong to identify what the problems might be.
 
If you are sending the samples for an audio sound signal to the DAC it should come out as an audio sound.....no crunching.
Does your portable speakers have standard Audioin/Linein jack...??....If you plug the speakers into your phone or PC do they work..??
If yes then they should work on teensy.
Can you run Audacity on your PC or have a scope to see the waveform..??....does it look anything like the shape it should be for your sound...?
Run a simple example from from the examples on the PJRC web or run the sinewave example and see do you get clean output, that way it will eliminate any problems with your code.
What are you sending to analogWrite statement..??.....Post some code so we can see..??
 
Thanks for the prompt Teenfor3 - based on your request for a code snippet I had another look at my code that pushes samples to the DAC pins, and I've used an unsigned value in the calculation by mistake, which means that any -ve part of the wave will basically result in maximum output to DAC (4095) because of assigning a -ve number to a uint16_t! Gulp. Amateur mistake :( I'll look at this when I get home, but pretty sure it will get rid of the crunchy noise!

I don't have a scope, but good tip on using Audacity to analyse the audio. Would I run the DAC to mic inputs to achieve this and record via audacity? The only alternative I can think of is to save the raw sample data to an AU file (or similar) on SD card and transfer it to the PC to check it an Audacity.

A little image to illustrate what I think is happening in my sample output:
waveforms.jpg

Code:
// _buffer is a float array of sample pairs

uint16_t l_smp = 2048 + (uint16_t)(_buffer[_buffertail++] * 4096);
uint16_t r_smp = 2048 + (uint16_t)(_buffer[_buffertail++] * 4096);
// So this part: (uint16_t)(_buffer[_buffertail++] * 4096);
// will be pushed to MAX_INT if the sample is in any way -ve :(

// Basic mistake. Should be:
	int16_t l_smp = 2048 + (int16_t)(_buffer[_buffertail++] * 4096);
	int16_t r_smp = 2048 + (int16_t)(_buffer[_buffertail++] * 4096);
// Or let the compiler sort it out:
	int16_t l_smp = 2048 + (_buffer[_buffertail++] * 4096);
	int16_t r_smp = 2048 + (_buffer[_buffertail++] * 4096);

l_smp = l_smp<0?0:l_smp>4095?4095:l_smp;
r_smp = r_smp<0?0:r_smp>4095?4095:r_smp;
#ifdef XM_STEREO
	analogWrite( XM_PIN_L, l_smp);
	analogWrite( XM_PIN_R, r_smp);
#else
	analogWrite( XM_PIN_L, (l_smp+r_smp)/2.0f );
#endif
 
Ok, I was wrong. That bug only existed in an older version of my code. Drat - I thought I was on to something :)
 
Hi, Projectitis,
the code you posted would not run on its own and over complicated to investigate a simple problem. All DAC needs is integers...????
I thought from the previous posts you were trying to find out in the DAC output was actually giving you an analog waveform out corresponding to waht you are generating in your code. If your code is generating 12 bit or less integer values sent to analogwrite anf it does not go negative at the final step then it should output a valid analog waveform. I thought the best way would be to try a simple audio example such as the sinewave example. The DAC is pure analog out .........not PWM and needing filtering........but at least a small coupling capacitor would block dc and a simple LP filter would improve but not really needed to see if it actually works.
I have pasted here the full code of a very simple sawtooth DAC test for you to try and you will see the values needed to make DAC work......then get your code to output the same range of values......no negatives..... Post again if you don't understand it.

You enquire should you use mic in or linein on you PC when recording to Audacity.......Definitely...LineIn.......The signals from Teensy are actually quite large...even though you may not think it. In may post #13 I suggested plugging into a known working amplified speaker or you mobile phone....these are LineIn so read post#13 again and if any query ask.
If you want I can post screen dumps of Audacity recording waveforms of this test sawtooth....but hopefully you will be able to run it .....into LineIn......click the record button and the teensy output should be seen going across the screen. The amplitude will probably be far too loud so turn down the Audacity slider control. You can the stiop recording after a few seconds and Zoom in and see the the waveform.......It should be a clean sawtooth........might be a wee bit of ringing at top peaks if you have no LP filter but the LineIn should sort that out and the dc blocking........then change the values and see what happens......but id correct combination is selected you should always get a sawtooth


Code:
// Simple DAC Sawtooth wave test on Teensy 3.2
// Change the saw1 and saw2 and DCbias values and see what happens + and - values
// keep within 12 bit values going out to DAC
// No timing involved, just runs at the speed of loop
// So the larger the number in the count in the for next loop 
// the lower the frequency
// Also at higher frequency you can see the effect on amplitude output
// and effect of low pass filters for change in freq.
//
// Note the final calculation to DAC must be always positive
// so...... ( i + DCbias ) must be GT Zero......0 to 4095 max


   int i = 0;
   int saw1 = 0;
   int saw2 = 1000;
   int DCbias = 0;

void setup() {
  analogWriteResolution(12);
}

void loop() {
   for (i=saw1; i< saw2; i++) {
   analogWrite(A14, i + DCbias);
  }
}
 
Well, I have the pre-amp board hooked up. Good news is that it works (amplifies). bad news is that the sound is as crap as before, JUST VERY LOUD! :D
 
Your are not saying what you are doing...??
How have you it hooked up..??
What sketch are you using..is it the one in post #16..??
What is the output coming from DAC A14 on its own....Does this look like a sawtooth....Does it sound like a sawtooth..??
It does not need to be amplified to be heard.....the gain of your amp is probably far to high....
Look at it in Audacity or on a scope....
With the proper settings indicated in #16 it should look like a beautiful sawtooth wave and sound a very pleasing tone to a musical ear.......
 
Hi Teenfor3 - I don't really have a question at this stage. I was just reporting back with the status of my project.

I've ported an xm music player to Teensy and am debugging it.

I've now created a wave file on the Teensy with the raw sample output of the xm player and saved it to SD to transfer to the PC. Playing this WAV back in Audacity results in perfect audio and shows expected waveforms. I'm pretty sure that the problem is in my playback code that drives the DAC pins, so that's the part I'm working on now. I wrote my own ringbuffer code, so it's likely samples are being skipped or something like that to result in crunchy sound.

So just to reiterate, I don't have any questions at this stage, just informing on progress.
 
Hi all,

ok, now I DO have a question!

My XM player is generating audio samples in the range -1.0f < s < 1.0f (in practice, the range is more like -01.f < s < 0.1f).
I have verified that the samples are correct by generating a WAV file and playing this on the PC. Sound is perfect.
On Teensy, I am converting the samples to the range 0 < s < 4095 and sending this value to the DAC pins (where 2048 is equivalent of sample = 0).
My DAC pins are feeding the L and R channels of a preamp.
My preamp VCC+ is set to 3.3v and VCC- is set to GND.

Is this correct? Does the speaker expect a value that is always greater than 0? Or, when the sample is < 0 (e.g. -0.2f) should the voltage to the speaker be -ve also?

I'm thinking that when the sample is below 0 (e.g. -0.2f) the voltage sent to the speaker should be -ve (i.e. -3.3v * 0.2 = -0.66v). Should I actually connect VCC- on the preamp to -3.3v instead of GND?
 
My XM player is generating audio samples in the range -1.0f < s < 1.0f (in practice, the range is more like -01.f < s < 0.1f).
If these are the actual samples of the audio in...then all you need to do is multiplyeach sample by 2000 and add 2000, that will convert it to 0 --- 4000 which is OK for sending to analogwrite...OR multiply by 1000 and add 1000 to convert to 0 --- 2000 for a lower amplitude......

I have verified that the samples are correct by generating a WAV file and playing this on the PC. Sound is perfect. .......... This verifies that you saved the -1--0--+1 samples to SD I assume on teensy (else you verified nothing) and played it on a PC using PC software and PC reading the SD....It didnt verify anything about the signal produced by teensy.......

On Teensy, I am converting the samples to the range 0 < s < 4095 and sending this value to the DAC pins (where 2048 is equivalent of sample = 0). ...... Are you assuming this is what your are doing..??.. or have you verified or measured it......as indicated before all you need is multiply by 1000 and add 1000....

My DAC pins are feeding the L and R channels of a preamp.
My preamp VCC+ is set to 3.3v and VCC- is set to GND. .........this is the requirements of the Pre Amp...read the datasheet.......it is separate from teensy bit of the project.
Use a capacitor on Teensy DAC out to block the DC bias.

Is this correct? Does the speaker expect a value that is always greater than 0? ......The speaker is connected to output of the amp..
Or, when the sample is < 0 (e.g. -0.2f) should the voltage to the speaker be -ve also?.........not required.....read the amp data sheet

I'm thinking that when the sample is below 0 (e.g. -0.2f) the voltage sent to the speaker should be -ve (i.e. -3.3v * 0.2 = -0.66v). Should I actually connect VCC- on the preamp to -3.3v instead of GND?........ No....read the datasheet....
 
I've solved it. It was an issue with me and the compiler having a different understanding of what was happening.

This code converts the float sample (-1.0f < s < 1.0f) to the range 0 < s < 4095
This code looks correct, but has a fatal flaw:
Code:
int16_t l_smp = 2048 + (sample * 2048);
The flaw with this code is that the compiler casts "sample" to an integer before multiplying by 2048, which means "sample" is only ever -1, 0 or 1. This is what the compiler is doing:
Code:
int16_t l_smp = 2048 + ((int16_t)sample * 2048);
This is what I thought the compiler was doing:
Code:
int16_t l_smp = 2048 + (int16_t)(sample * (float)2048);

This is the correct code. Note the only change is specifying that the number literal is a float:
Code:
int16_t l_smp = 2048 + (sample * 2048.0f);

Anyway, I now have crystal clear, beautiful music from my XM player. I'll clean up the code a bit and then commit it to the github for anyone who is interested.
 
I've solved it. It was an issue with me and the compiler having a different understanding of what was happening.

This code converts the float sample (-1.0f < s < 1.0f) to the range 0 < s < 4095
This code looks correct, but has a fatal flaw:
Code:
int16_t l_smp = 2048 + (sample * 2048);
The flaw with this code is that the compiler casts "sample" to an integer before multiplying by 2048, which means "sample" is only ever -1, 0 or 1. This is what the compiler is doing:
Code:
int16_t l_smp = 2048 + ((int16_t)sample * 2048);
This is what I thought the compiler was doing:
Code:
int16_t l_smp = 2048 + (int16_t)(sample * (float)2048);

This is the correct code. Note the only change is specifying that the number literal is a float:
Code:
int16_t l_smp = 2048 + (sample * 2048.0f);

Anyway, I now have crystal clear, beautiful music from my XM player. I'll clean up the code a bit and then commit it to the github for anyone who is interested.

I am interested :)
 
I dont think the above complication/problem exists in Arduino IDE......
Can the "f" suffix be used in Arduino....if yes...how..??
Does the code below test for your problem.... It doesn't seem to matter whether ampsmp is float or integer it still works..???

Code:
   float i = 0.0;
   float ix = -1.0;
   float iy = 1.0;
  // float ampsmp = 1000.1;
   int ampsmp = 1000;
   int DCbias = 1000;

void setup() {
  analogWriteResolution(12);
}

void loop() {
   i=i+0.01;
   int16_t l_smp = DCbias + (i*ampsmp); 
   analogWrite(A14, l_smp);
   if (i >= iy) i=ix;
}
 
Status
Not open for further replies.
Back
Top