inquiry about audio sample, confused of recorder samples

Good day fellas,

audio programming is interesting and i am a bit on starting to learn some recording and playing and manipulating sample audio samples

I would like to ask some comments or suggestion or answer to my queries.

I have a recorder app, the settings for the recorder is 96000 sampling rate 8 bit wave pcm…mono
first, I am confused, I am getting values like values from -128 to + 127…the data holder is signed char…I am expecting signed waves with 0 in between…
second, I want to try also to filter the recorded value using bandpass filter(http://www.exstrom.com/journal/sigproc/)…
does this sample values that i received has no problem with when I pass it to the filter? or do i need to convert the values?

because after the applying the bandpass filter, it resulted to weird data.

and maybe you have also a quick helpful reference in using bandpass filter with easy C sample code…DSP is a big thing for me, i am newbie here…

http://www.exstrom.com/journal/sigproc/ <<—does this link helpful for bandpass filter?

Signed 8 bit audio should have values between +127 and -128 (see here: Signed number representations - Wikipedia )
For processing in Audacity these values would be converted to a range of +/- 1 in 32-bit float format.

thanks steve…so i need to convert it to -1 to 1 range

my sample data charSamples[ARRAY_SIZE] an array of signed chars
which one i am using below?do I need to subtract 128 from the sample?

floatSamples = (charSamples-128)/32768.0
or
floatSamples = (float)charSamples/32768.0

another question:
is the following code okay for converting float to signed 8 bit? because after
for (int i = 0; i <SampleCount; i++)
charTo = (byte)(128 + ((byte)(floatFrom*(127.0))));

I’ve no idea if you need to convert :wink: I was just saying what happens in Audacity.

If you do convert to 32-bit and then convert back to 8 bit integer, you should really use dither to randomize quantize errors when down-sampling from 32 to 8 bit. On the other hand, if you keep the data in integer format then it might simplify the programming, but there will be significant rounding errors.

I’m afraid I can’t help with the programming aspects as I’m not a programmer. I’ll see if I can get someone that knows about programming to comment.



My disclaimer: while I am a programmer with some knowledge of Audacity, my forte is GUI and interface design with a bit of math. I think your math is wrong so let me try to explain my (limited) understanding…
As Steve says, signed 8-bit data spreads out between -128 & +127 inclusive (note because of the zero it is not -128 through +128).
-128…-1…0…+1…+127
we need to map those values to signed floats between -1.00 and +1.00 inclusive (note because of zero there will not be a one-to-one ratio that is the same for both positive and negative numbers):
-1.00…0…-1.00
So, we will need two*three (6) “formulas” – one set for 8-bit to float and one set for float to 8-bit; thus two each of:
one to deal with negative values
one trivial one to deal with zero so we don’t see any divide-by-zero errors
and one for positive
So we have a compound conditional:

if (value == 0) {
     do the zero formula
}
else if (value < 0) {
     do the less-than-zero formula
}
else {
     do the greater-than-zero formula
}

To convert 8-bit to float, assume we have inChar & inFloat as our input values, outChar, & outFloat as output results. The zero formula is obvious:

if (aChar == 0) {
     outFloat = 0f;
}
and
if (outFloat == 0f) {
     aChar = 0;
}

Now we need to map the non-zeros:

if (aChar > 0) { //positive char to positive float
     outFloat = ;
}
and
if (aChar < 0) {//negative char to float
     outFloat  = ;
}
if (outFloat > 0) { //positive float to positive char
     aChar = ;
}
and
if (outFloat < 0) {//negative float to char
     aChar = ;
}

To see the way the ratio works lets pretend that a char is -4 to +3 (8 values as opposed to 256) and do all the calculations one-at-a-time to “prove” the math. So we will map chars -4 to -1.00, 0 to 0, and +3 to +1.00:
-4 = -1.00
-3 = -0.75
-2 = -0.50
-1 = -0.25
0 = 0
1 = +0.33333333…
2 = +0.66666666…
3 = +1.00
As you can see, ignoring the sign, the mapping of +2 and -2 are vastly different and the reason is obvious. The general formula for negative is floatValue equals (charValue divided by the number of negative integers)–in this case 4, in the real case MIN(signed 8-bit) which is 128. For positive use the same general idea but we need to use MAX(signed 8-bit) which is one integer smaller.

Mapping the same floats:
-1.00 = -4
-0.75 = -3
-0.50 = -2
-0.33 = -1
0 = 0
+0.33 = 1
+0.66 = 2
+1.00 = 3
The general negative case here is charValue equals (floatValue times the number of negative integers) and for positive charValue equals (floatValue times the number of positive integers); no divide-by-zero problem but zero still needs to be dealt with.

char-to-float (where a divide-by-zero error could occur):
if (aChar < 0) {//negative char
     outFloat  = aChar / 128;//note, need much better code here, bad rounding & variable promotion
}
else if (aChar > 0) {//positive char
     outFloat  = aChar / 127;note, need much better code here...
}
else{// (aChar == 0)
     outFloat = 0f;//good enough code
}

and for float-to-char
if (outFloat <= 0f) {//less than or equal to zero
     aChar = outFloat  * 128;//note, need much better code here, bad rounding & variable promotion
}
else {
     aChar = outFloat  * 127;//note, need much better code here...
}

outFloat = aChar / 128;//note, need much better code here, bad rounding & variable promotion
In the above the note is talking about how the computer deals with the division–since both are integers it will be done using integer math and rounding routines. What we need to do is promote everything to the highest resolution variable possible before doing the math then use the proper rounding formula:

if (aChar < 0) {//negative char
     float tempChar = (float)aChar;//promote char to float
     float intermediateValue = tempChar  / 128.0f;//note we promote 128 to a float
     outFloat  = round(intermediateValue);//see round() below;
}
float round(float r) {//symmetrical round
    return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}

aChar = outFloat * 128;//note, need much better code here, bad rounding & variable promotion
In the above the note is talking about the same thing. The extension of the other simplified formulas is left to the reader !




Don’t let Steve Steve fool you, he is a great programmer with lots of released code. What he means to say is that he does not program in a C-style language (C++ etc.), he programs in Nyquist a highly specialized language for writing audio related plug-ins.

As to your formula see my answer above.

From a PM:

That is why my disclaimer ! I can do the math but have no real understanding of the audio standards. From what I can see, +1.0 is valid – why does Audacity ban it?

The solution I proposed could be expanded to deal with a return of +1.0f:

char-to-float (where a divide-by-zero error could occur):

else if (aChar > 0) {//positive char
     outFloat  = aChar / 127;note, need much better code here...
     if (aChar == 127) {//would yield +1.0f which is unwanted
          outFloat = outFloat - MIN(float);//  (MIN(float) may not be defined; bitshift is not implemented for floats as far as I know but would be a better theoretical choice)
     }
}

I believe most DSP algorithms assume floating point.

Actually, 8-bit WAV files use unsigned integers (0-255). Silence is 128. So, you generally have to subtract 128 and convert to signed before doing any conversions or before trying to visualize the waveform. [u]Here[/u] is a reference. (Look under “Notes”.) 16 bit WAV files use signed integers.


…there is no +1.0 for integer formats.

Maybe because there is +1, but no “point zero” in integers???


\

  • I wasn’t sure if silence was 127 or 128, so I created an 8-bit silent file and looked at it with a hex editor. It was full of 80 hex (128 decimal).

I totally agree that is what an 8 bit RIFF file should be, but I’m confused by the original post:

What is this “recorder app,”?
Recording at 96 kHz and 8 bit is a very odd combination for audio, and even more so if it is signed 8-bit data.

I think DVDDoug has more knowledge on the topic than I and I will happily bow to his knowledge! I was asked a specific mathematical question – how to do something in a C++ program. I was very careful to preface my reply with a disclaimer which I will now paraphrase – I am a “pretty good” GUI/interface programmer and a fair mathematician but I know next to nothing about audio formats. I suspect that my answer was in-depth enough to explain the process and a reasonably intelligent programmer can plug in the appropriate MIN and MAX values for the non-float variables and adopt my code as appropriate.

Thank you very much, I do now understand a little bit.

@ steve: yeah, its a little bit odd but its a good start for me.

@ DVDdoug: thanks for the link

@Edgar: That’s so detailed…thank you very much.

One more question:

Do playback have the same scenario?
That we have to fed a 8bit signed(+127 to -128) also to the audio?
because I created a sine wave tone (from this formula: Amplitudesin(2PiFreqTime)…Amplitude used was 120
currently what i did was, I cast the output of the formula to integer and added 127; and kinda have a weird unwilling noise of the tone.

I think that you need to tell us more about what you are doing.
Are you using a standard computer sound card or some other “special” hardware?
Why is your audio 96 kHz, signed 8 bit? Is this a feature of your hardware or just an arbitrary format that you decided to use?
What are you trying to achieve?