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