problem. Initially, I had set the sampling frequency at 8000 Hz, with
PCM at 16 bits, signed, little-endian, channel mono. However, with
this sampling frequency, I started getting Java OutOfMemoryException.
So, I shifted to 2000 Hz. Also, I am using a software tone generator.
You *must* set your tone generator to a lower frequency! At
that frequency, even if your software is perfect, you're still
going to see garbage data!
Nyquist's sampling theorem says that when you sample at 2000 Hz,
the highest possible frequency you can represent at *all* (without
completely mangling it) is 1000 Hz. There must be at least two
samples per wavelength.
And that 1000 Hz is in an ideal world. A real-world A-to-D
converter has a low-pass filter that will filter out everything
below the Nyquist frequency (in this case 1000 Hz), and the slope
of that filter is usually sharp, but it is not infinite. That
means in practice the highest frequency that the A-to-D converter
will even see is something less than 1000 Hz.
I would try setting your frequency generator to something like
100 Hz, or set your sampling rate higher.
However, when I convert the bytes to float values, I do not see the
periodic sinusoidal data, as expected, (sample output below)
18770.0
38724.0
16727.0
28006.0
16.0
1.0
2000.0
4000.0
2.0
24932.0
38688.0
0.0
0.0
0.0
0.0
I understand that with 16 bit resolution, I can get numbers in the
range -2^16 - 1 to 2^16 - 1.
No, that would be a total of 2^17 + 1 distinct values. With a
16-bit number, you can only have 2^16 distinct values.
The usual format for signed numbers is two's complement. In
that format, the values range from -2^15 to 2^15-1, which is
another way of saying from -32768 to +32767.
I believe that I am not converting the data correctly. To achieve the
conversion, I am taking 4 bytes at a time, and converting them. That
is, first bytes 0 - 3, then bytes 4 - 7 and so on. Is this correct ?
Well, you haven't said whether the data in your input file is
monophonic, stereophonic, or something else. If it's stereo,
you're going to have pairs of samples. Since each sample is
16 bits, which is 2 bytes, each pair of samples will be 4 bytes.
But I would avoid that at the early stages and try to start with
an input file that is monophonic in order to keep things simple.
Assuming you have a monophonic input file, you need to read
only 2 bytes per sample.
Any hints, suggestions would be greatly appreciated. Thanks in advance
for your help.
Let's assume you have read some bytes of the input file into
some array. Converting that into samples is going to look
something like this:
byte[] rawBytes = getBlockOfSamples();
if (samples.length % 2 != 0) {
throw SomeException("Can't handle samples spanning blocks");
}
short[] samples = new short[samples.length / 2];
int inputOffset = 0;
int outputOffset = 0;
while (inputOffset < samples.length) {
// read in both bytes of first sample;
// put them in 16-bit types since they'll
// be converted to that size soon anyway.
short lowOrder = rawBytes[inputOffset];
short highOrder = rawBytes[inputOffset+1];
inputOffset += 2;
// the low-order byte is meant to be
// unsigned since the sign bit is in the
// high-order byte. But the java type
// wraps around after 127, so some of
// our positive numbers will have gotten
// converted to negatives. so fix that.
// since we have already converted to short,
// we can already handle the larger range.
if (lowOrder < 0) {
lowOrder += 256;
}
// shift the high-order byte into position
// and combine them.
samples[outputOffset] = lowOrder | (highOrder << 8);
outputOffset++;
}
There is probably some tricky way to avoid that conditional I
used to correct for the negative values, but let's forget about
performance for now.
- Logan