Resampling is a pretty complicated topic. There are a few different approaches you can take.
Linear Interpolation is pretty decent, usually. In this case, since you're cutting the samplerate clean in half, linear interpolation would have the same effect as taking every other sample from the stream.
You could try to get clever and take the average of the two samples instead of just dropping one of them, but that wouldn't really be any better (still linear interpolation, just shifted a bit).
Other interpolation methods are far more complex (gaussian, cubic, etc -- try looking them up on wikipedia -- although they're not that easy to understand).
You said that linear interpolation sounds bad -- but I can't imagine it would really be that much worse when compared to another program's output. So I kind of have to point out the obvious here. Are you converting between signed and unsigned samples properly? 16-bit audio is typically signed, whereas 8-bit is typically unsigned. If you're just dividing the samples by 256 and not adjusting the sign, it's no wonder everything sounds horrid.
Provided that's the case (16-bit signed to 8-bit unsigned), be sure to flip the high bit after you shift the sample to the right:
1 2 3 4 5 6
|
unsigned char Make8bit(signed short sample)
{
sample >>= 8; // drop the low 8 bits
sample ^= 0x80; // toggle the sign bit
return (sample & 0xFF);
}
|
But of course... 8KHz (and 8-bit at that) is pretty much always going to sound terrible no matter which way you slice it.