Re: PWM -> Audio Output
- From: Rene <a@xxx>
- Date: Fri, 12 Jun 2009 22:47:31 +0200
eeboy schreef:
To start, I have solved the popping issues using the sine look up table
suggestion above. Turns out it's got nothing to do with the audio specific
source code. I had a corrupted page at the start of each FLASH sector (4k).
With an 8kHz sampling rate that produced a nice periodic pop at a rate of
2Hz. Feeling quite foolish on that...
Don't ;-)!
Audio is now quite nice but I think it could still use some refining.
There is a faint high pitch whine (which will hopefully disappear when I
lower the cutoff point) and a bit of static throughout (although I could
certainly live with it given the end use).
Do You hear this static as well as You play the sinus or maybe just 0 all the time? Do You hear it as well when You connect the input of Your output amplifier to ground? Just trying to check where this static originates.
As Rene has pointed out my post lacks some important information (sorry)
and so here it is:
So first get straight what the parameters You are talking about, reallyare:1. Clock frequency of the micro: ?
50 MHz (Micro = LMI PN:LM3S1439)
2. Prescaler setting in pwm-timer: ?
Clock driving the PWM module is scaled by 2
3. Resolution of timer in it's current pwm-configuration: ?
16 bit
4. Oversampling number: ?
4x currently
5. Resolution of Your samples in the RAW file: ?
RAW file contains unsigned 8bit
So:
Clock speed -> 50 MHz
Timer speed -> 50 / 2 = 25 MHz
Timer resolution -> 16 bit
What is important in using a timer for pwm is the interrupt/reset value. For an 8 bit sample resolution, this should be 0xFF. That is the pwm resolution we are talking about. In this case the timer resolution may be 16 bit, the pwm resolution is 8 bit. This means it counts from 0 to FF, generates an int and resets itself to 0 to start counting again. Somewhere during counting the pwm pin level is toggled, depending on how You configured it. This cycle takes 256 clock ticks.
Should You have chosen phase correct pwm, the timer will count down after having reached the top and generate the int when it is at 0 again (toggling the pin once when counting up and toggling it again when counting down). That way every cycly takes 512 clock ticks.
But now some math with these numbers. The clock tick for the timer comes 25.000.000 times per second. If You are using an 8 bit pwm resolution (which can be in a 16 bit timer, no problem there, just configure the TOP value to be 0xFF), one cycle will take 256 ticks. So that means You can have almost 100.000 cycles per second. You need at least 8000 cycles per second for the sampling frequency, that leaves You with a maximum of 12x oversampling (and 6x times when using phase correct pwm). Therefore, the numbers 32 and 64 You mentioned, are not possible and will cause strange things to happen. Which fits in with Your remarks about the sound getting better when decreasing number of oversampling.
I asked You the wrong question, I asked the timer resolution but I meant the pwm resolution, the bit width of this TOP value I mentioned. If You are actually outputting at 16 bit pwm, it will give a very low output volume. In fact so low that I presume You are actually using the correct pwm resolution (and this is off course just as good in a 16 bit timer as it is in an 8 bit timer).
If You are using 12x oversampling You need not worry about Your output filter (with Your 4x I can imagine the high pitched sound may be caused by it, just increase the oversampling and see what happens), the thing You have now with the resistor You had lying around is more than sufficient.
I am however still curious whether all these assumptions are actually true. I would be a good idea to put a 1 on some I/O pin when You enter the ISR and turn it of the next time You enter it. That way You can use Your scope to see what output sampling frequency is actually being used and whether it is truly regular. When You hear a high pitched sound, it might be 8 Khz, and in that case I suspect the oversampling not be taking actually taking place, because then the sound You want to have filtered out at the output, the tone that is added because of the mere fact that You are playing back discrete samples, would have to be a multitude of this frequency being more difficult to hear for Your ears, not in the least because 16 kHz (I am not even going to mention 24 and up) will be filtered out quite efficiently.
You can also download spectrum analyzers on the web, just google for them, that way You might easily see what frequency this high pitched tone has. I mean software analyzers, You connect the output of Your digital sound processing system (sound impressive! ;-)) to the input of the soundcard, can be quite handy sometimes!
I apologize for underestimating Your knowledge previously, it was the way You put Your initial question, a little bit chaotic (no offense meant!), that made me suspect this, however, this followup is showing quite the opposite. And the fact that Your flash was broken, well, who would have thought of that. When experimenting with some technique, usually one suspects one's own experiments. Allthough... Very often I blame the compiler for generating errors in my programs! The times I actually managed to pin the error down on an actual compiler error (always optimizations that work "too well") are quite rare however, usually it turns out to be my own mistake after all.
6. Sampling frequency at which the samples in the raw file have been taken: ? (I presume this is 8 kHz.)
8kHz
How have the samples been taken? Did You, if You took them Yourself, use
a better input filter as the output filter You are using now?
All samples have been taken on the PC via a mic or are converted from
CD's. I am trying all different types (with respect to the frequency band)
at the moment. In the end it will mainly be voice prompts. I am playing
back the files on the PC (in RAW form) to create a benchmark of quality.
Your story is very unclear, if You fill in the values above things might
get a bit clearer. Perhaps also mention what micro You are using, posting some sourcecode of the program You are using wouldn't be a bad idea as well.
The most relevant piece of source code is below.
Actually I wanted to see how You configured the timer but after having read the above and knowing that the sound is OK now AND knowing now that I don't have any experience with the micro You are using, it would not have added much after all.
It is the PWM int where
everything happens. There are a few things you'll need to know to make
sense of it. First, I have implemented two buffers for obvious reasons.
It is not that obvious to me. Why use a buffer when the audio You want to play is sitting around in memory? Or do You load them from something or so? I am not saying the buffers are not necessary, I just don't understand why they are necessary and am curious. If I want a micro to play samples I just insert them in my source as tables and then just retrieve the samples from this table at runtime.
When one buffer is emptied its service is scheduled (well within the bounds
of the other exhausting). Next, g_ulSampleCount stores a count which
compares against the oversampling multiplier. So, the PWM width is changed
every AUDIO_PWM_FREQ_MULT times (currently 4). I have not yet made an
attempt at smoothing (interpolating) between the current sample and the
next. Trying to think of an elegant way to do that.
void audioInt(){
char cWidth;
//Clear int
PWMGenIntClear(PWM_BASE,PWM_GEN_0,PWM_INT_CNT_ZERO);
//Is it time for a PWM update? (based on sampling rate of audio and PWM
freq)
if(g_ulSampleCount++>=AUDIO_PWM_FREQ_MULT){
//Reset count
g_ulSampleCount=1;
//If both buffers exhausted, stop PWM
if(0==audioBuffer_A.uiBufferCount + audioBuffer_B.uiBufferCount){
//Disable generator and shutdown amplifier
ampPowerDown();
PWMGenDisable(PWM_BASE,PWM_GEN_0);
PWMOutputState(PWM_BASE,PWM_OUT_0_BIT,false);
}
//If buffer A exhausted, switch to B
else if(0==pBuffer->uiBufferCount && pBuffer==&audioBuffer_A){
//Point to next buffer
pBuffer=&audioBuffer_B;
//Schedule buffer service
deltaqInsert(audioBuffer,AUDIO_BUF_SVC_INTERVAL);
}
//If buffer B exhausted, switch to A
else if(0==pBuffer->uiBufferCount && pBuffer==&audioBuffer_B){
//Point to next buffer
pBuffer=&audioBuffer_A;
//Schedule buffer service
deltaqInsert(audioBuffer,AUDIO_BUF_SVC_INTERVAL);
}
//Scale width
cWidth=*(pBuffer->pcBuffer++);
PWMPulseWidthSet(PWM_BASE,PWM_OUT_0,cWidth*PWMGenPeriodGet(PWM_BASE,PWM_GEN_0)/256);
pBuffer->uiBufferCount--;
}
}
Your indentation is not perfect when it comes to if-else, I really think this is important because it tells one much about the program without having read a single word.
Considering all of this... any suggestions for improvement? Anything stand
out? Can anyone elaborate on preemphasis and how to apply it in my case?
You're in the position that You can do quite some oversampling. That way the cut-off frequency from Your output filter can be quite high (like it is because You did not have the correct part at hand) and the influence of the filter at the top of the spectrum You are using will be small. I don't think preemphasis will pay off in this case and I would not bother.
I wish You good luck and fun with Your project, it looks both interesting and promising. Let us know how things progress, I for one am very interested.
Yours sincerely,
Rene
.
- Follow-Ups:
- Re: PWM -> Audio Output
- From: eeboy
- Re: PWM -> Audio Output
- From: Rene
- Re: PWM -> Audio Output
- References:
- PWM -> Audio Output
- From: eeboy
- Re: PWM -> Audio Output
- From: Jim Stewart
- Re: PWM -> Audio Output
- From: Rene
- Re: PWM -> Audio Output
- From: eeboy
- PWM -> Audio Output
- Prev by Date: Re: what schematic and component library for FreePCB ?
- Next by Date: Re: PWM -> Audio Output
- Previous by thread: Re: PWM -> Audio Output
- Next by thread: Re: PWM -> Audio Output
- Index(es):
Relevant Pages
|