On Aug 1, 11:31 am, KWhat4 <heispsycho...@gmail.com> wrote:
> I am very new to dsp in general and I am attempting to convert 16khz
> PCM stream (byte array) to 8khz stream (byte array) in Java. I have
> figured out that I can copy over every other frame from the 16khz
> stream to the 8khz one but I get what I think is called dithering?
Aliasing. Your original 16k sample/sec stream probably
contained frequency content from 20 Hz or so to nearly
8 kHz. If so and you didn't do any filtering, then your
8k sample/sec stream will contain not only 0 to 4 kHz
sound, but also 4 to 8 kHz sounds folded down in frequency
right on top of and mixed up with the 0 to 4 kHz sound.
So before you decimate (reduce the sample rate by throwing
away samples) you need to low pass filter so that there
is very little content at or above half the new sample
rate (or you can filter as you decimate in one step).
> My question are the following:
>
> How do I clean up the audio so it sounds close to normal?
You need to filter before decimating. Your "dithered"
sound already has noise mixed in that can't be removed
(without reference to the original signal).
> Is there a better way todo this insted of copying some ratio of
> frames?
Copying a ratio of frames will work if each channel of
the signal has been appropriately low pass filtered beforehand
or is during the copy. You have to filter each channel
separately, so you may have to decompose and recompose
the frames before/after filtering.
If you don't care about optimization, then the filtering
can be done in only a few dozen lines of code.
Here's some untested, quick and dirty code (in Basic !)
which might filter one channel, and even interpolate if
needed:
---cut here---
rem - QDSS Windowed Sinc ReSampling subroutine in Basic
rem
rem : x = new sample point location (relative to old indexes)
rem (e.g. every other integer for 0.5x decimation)
rem : indat = original data array
rem : alim = size of data array
rem : fmax = low pass filter cutoff frequency
rem : fs = sample rate
rem : wnwdth = width of windowed sinc used as the low pass filter
rem - resamp() returns a filtered new sample point
sub resamp(x, indat, alim, fmax, fs, wnwdth)
r_g = 2 * fmax / fs : rem Calc gain correction factor
r_y = 0
for r_i = -wnwdth/2 to (wnwdth/2)-1 : rem For 1 window width
r_j = int(x + r_i) : rem Calc input sample index
: rem calculate von Hann Window. Scale and calculate Sinc
r_w = 0.5 - 0.5 * cos(2*pi*(0.5 + (r_j - x)/wnwdth))
r_a = 2*pi*(r_j - x)*fmax/fs
r_snc = 1 : if (r_a <> 0) then r_snc = sin(r_a)/r_a
if (r_j >= 0) and (r_j < alim) then
r_y = r_y + r_g * r_w * r_snc * indat(r_j)
endif
next r_i
resamp = r_y : rem Return new filtered sample
end sub
rem * Note that fmax should be less than half of fs, and less
rem than half of new_fs (the reciprocal of the x step size).
rem * Filter quality increases with a larger window width.
rem The wider the window, the closer fmax can approach half
rem of fs or new_fs.
rem * Note that several operations inside the FOR loop can be
rem precalculated.
rem * There are more optimal windows than the von Hann window.
rem * Note that if the x step size is rational the same Window
rem and Sinc values will be recalculated repeatedly; therefore
rem these values can either be cached, or precalculated and
rem stored in a table; or interpolated from a smaller
rem precalculated table; or computed with a low-order
rem polynomial fit to each lobe between zero crossings
rem of the windowed sinc. (Performance optimization is
rem left as an exercise for the student).
rem Ron Nicholson's QDSS ReSampler cookbook recipe
rem QDSS = Quick, Dirty, Simple and Short
rem Copyright 2007 Ronald H. Nicholson Jr.
rem Version 0.1 - 2007-Aug-01
rem No warranties implied. Error checking, optimization, and
rem quality assessment of the "results" is left as an exercise
rem for the student.
rem (consider this code Open Source under a BSD style license)
rem IMHO. YMMV. http://www.nicholson.com/rhn/dsp.html
Reply by Erik de Castro Lopo●August 1, 20072007-08-01
KWhat4 wrote:
> I am very new to dsp in general and I am attempting to convert 16khz
> PCM stream (byte array) to 8khz stream (byte array) in Java.
I assume your audio is 16 bit PCM. You need to operate on it as such.
> I have
> figured out that I can copy over every other frame from the 16khz
> stream to the 8khz one
That s effectively zero-order hold resampling. It will not sound
good.
> My
> question are the following:
>
> How do I clean up the audio so it sounds close to normal?
Once you audio has aliasing noise it is not really possible to clean
it up again (the eggs cannot be unscrambled). What you need to do
is go back to the clean signal and resample it correctly.
> Is there a better way todo this insted of copying some ratio of
> frames?
Yes, for your particular case, you need to apply a low pass filter
that blocks frequencies higher than 4 kHz and then dropping every
second sample will sound just fine.
The more general case is much more complicate.
> Are there some example snipits to do this sort of thing? (Java / C /
> Sudo)
I am the author of a high quality audio resampler realeased under the
terms of the GNU General Public Licence:
http://www.mega-nerd.com/SRC/
Commercial use licenses are also available at reasonable rates.
HTH,
Erik
--
-----------------------------------------------------------------
Erik de Castro Lopo
-----------------------------------------------------------------
BSD: A psychoactive drug, popular in the 80s, probably developed at UC
Berkeley or thereabouts. Similar in many ways to the prescription-only
medication called "System V", but infinitely more useful. (Or, at least,
more fun.) The full chemical name is "Berkeley Standard Distribution".
Reply by KWhat4●August 1, 20072007-08-01
I am very new to dsp in general and I am attempting to convert 16khz
PCM stream (byte array) to 8khz stream (byte array) in Java. I have
figured out that I can copy over every other frame from the 16khz
stream to the 8khz one but I get what I think is called dithering? My
question are the following:
How do I clean up the audio so it sounds close to normal?
Is there a better way todo this insted of copying some ratio of
frames?
Should I really be using PCM or is some other format more effective/
easier/faster?
Are there some example snipits to do this sort of thing? (Java / C /
Sudo)
Thanks in advance.