• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 351
  • Last Modified:

Audio formats conversion bug

The following code is a short Java function that converts an array of integers of length X holding RAW PCM data to an array of bytes, also of length X, holding ULAW data.
For some reason the output is of very poor quality.

Can anyone take a guess ?


private void Linear2Ulaw (int[] pcm_vals, byte[] ulaw_vals,
int range)
{
    int sign, exponent, mantissa;
    int exp_lut[] =
{0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4
,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7
,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
,7,7,7,7};

  for (int i=0; i<range; i++)
  {
   //Get the pcm_vals into sign-magnitude.
   sign = (pcm_vals[i] >> 8) & 0x80;       //set aside the sign
   if (sign != 0) pcm_vals[i] = -pcm_vals[i];  //get magnitude
   if (pcm_vals[i] > CLIP) pcm_vals[i] = CLIP;  //clip the magnitude

   //Convert from 16 bit linear to ulaw.
   pcm_vals[i] += BIAS;
   exponent  = exp_lut[(pcm_vals[i] >> 7) & 0xFF];
   mantissa  = (pcm_vals[i] >> (exponent + 3)) & 0x0F;
   ulaw_vals[i]   = (byte)(~(sign | (exponent << 4) | mantissa));
  }
}
0
theGrayPilgrim
Asked:
theGrayPilgrim
  • 9
  • 7
  • 6
  • +3
1 Solution
 
diakovCommented:
How about shifting the sign to << with 7 before the |?
0
 
theGrayPilgrimAuthor Commented:
If you mean something like this -

ulaw_vals[i]   = (byte)(~((sign << 7) | (exponent << 4) | mantissa));

I tried it, and the quality is as poor as before (sounds a little different though).

Anything else ?

TIA, Roy.
0
 
diakovCommented:
Ensure CLIP + BIAS does not exceed int.

I understand what you're doing but since I don't know this formulae I cannot do many thing about it. I'll pass it to someone...
0
Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

 
theGrayPilgrimAuthor Commented:
CLIP and BIAS are defined as follows -

private static int BIAS      = 0x84;      
private static int CLIP      = 8192;

The additional following information might be useful to you:

There is one case in which the conversion function does work - if I record the audio clip as 8bit RAW, save it to disc as 16bit RAW, and then convert it to ULAW, the quality is good. In all other cases (record and save as 16bit, or record and save as 8bit), the quality is very poor.

Thanks again for your efforts...  Roy.
0
 
BonevCommented:
theGrayPilgrim,
What are you trying to accomplish? Besides the problem that you don't get what you need, I've noticed several issues that may affect your program:
1) The size of int is 32-bit, but you work with the ints as they are 16-bit.
2) Don't use shifts unless it is necessary (for the determining the sign, for example, you could use sign = value < 0; )
3) Don't overwrite the passed parameters (you change the input array)

Regarding the algorithm, could you tell me what is ULAW and to give more detailed explanation of your goal?
I guess, the quality could be poor because of losing significant bits when using the exp_lut table.
0
 
theGrayPilgrimAuthor Commented:
Bonev,

ULAW coding is a form of compression for audio signals. It is widely used in the telecommunications field because it improves the signal-to-noise ratio without increasing the amount of data. Typically, ULAW compressed speech is carried in 8-bit samples.
As you also know, it is the only format supported by Java (.au).

As for the 3 point you bring up - I don't think it is number 3.
As for the other two, do you have any specific suggestions ?
I think you are right in what you say in number 1, The code I used is a C code which I converted to Java, so there might be a problem there.
0
 
diakovCommented:
'theGrayPilgrim',

- You do change the input array. You can run a simple test if you don't believe.
- The Java integer is 32 bit. So you do not exctract the sign correctly.

//So, some corrections:
for (int i=0; i < range; i++)
{
  int value = pcm_vals[i];
  //Get the pcm_vals into sign-magnitude.
  //now you have 1 in the 1-st bit as sign.
  sign = (value < 0)?1:0;       //set aside the sign
  if (sign != 0)
     value = -value;  //get magnitude
  if (value > CLIP)
    value = CLIP;  //clip the magnitude

  //Convert from 16 bit linear to ulaw.
  value += BIAS;
  exponent = exp_lut[(value >> 7) & 0xFF];
  mantissa  = (value >> (exponent + 3)) & 0x0F;
  ulaw_vals[i] = (byte)(~( (sign << 7) | (exponent << 4) | mantissa));
}

//and at the end apply

ulaw_vals[i] = (byte)(~((sign << 7) | (exponent << 4) | mantissa));


Let me know if this works.
0
 
diakovCommented:
I think I pasted some lines twice, sorry.
0
 
BonevCommented:
Roy,
Is it 16-bit C source code? Could you post it here?
Have you tried it with all the combinations - 16 vs. 32 bits?

0
 
BonevCommented:
I mean 8 vs. 16 bits
0
 
diakovCommented:
just in case. :-)

(I going out)
0
 
theGrayPilgrimAuthor Commented:
diakov & Bonev -

diakov - Sorry, but I have to reject your answer. I tried the changes you suggested, but the result came out the same. You and Bonev might be right regarding the 32/16 bit problem, but the changes you suggested just didn't do the trick.

Bonev - The original C code is at the following URL , http://www.ri.cmu.edu/comp.speech/Section2/Q2.7.html . If you have any problems with getting it, tell me, and I'll post it here. Until your first mail, I assumed that the C code needs no changing, so I didn't try anything else.

As for changing the original PCM array, I am ofcourse aware that I am changing it, what I meant is that it does not effect the final result of the function.
0
 
BonevCommented:
Roy,
I have looked at the original function - it converts signed 16 bit linear sample to 8 bit ulaw sample. The problem with 8 bit recording - 8 bit save could be the way you convert signed 8 bit raw value to int - you have to make a signed conversion.


0
 
theGrayPilgrimAuthor Commented:
Bonev,

What I am trying (unsuccessfully) to do is to take a 16 bit recording - 16 bit save  ,  and convert it to ULAW with the above function.
I take the 16 bit PCM file , read it into an array of integers using the following code -

 int[] i_arr = new int[numOfInts];
 int j=0;
 for (int i=0; j<numOfInts; i+=2)
 {
   i_arr[j++] = (int)(b_arr[i] | (b_arr[i+1] << 8));
 }

where b_arr is the byte array I got by reading the file.
The i_arr is the array of integers which is passed to the conversion function as an argument (pcm_vals[]).

Do you see any problems with that ?
0
 
gadioCommented:
theGrayPilgrim, I agree that your problem may result from the number of bits in int in java. The problem is that when you read 16 bit and place them in an int array you loose the sign compleatly. Any computation that use the sign information will fail. What you should do is change your arrays from int arrays to short arrays.
0
 
gadioCommented:
Heres the example, tell me if it works.

       short[] i_arr = new int[numOfInts];
       int j=0;
       for (int i=0; j<numOfInts; i+=2)
       {
         i_arr[j++] = (short)(b_arr[i] | (((short)b_arr[i+1]) << 8));
       }



and:


  byte exp_lut[] =
    {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4
    ,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
    ,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
    ,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
    ,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7
    ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
    ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
    ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
    ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
    ,7,7,7,7};
 byte sign, exponent, mantissa;
 for (int i=0; i<range; i++)
      {
       //Get the pcm_vals into sign-magnitude.
       sign = (byte)(pcm_vals[i] >> 8) & (byte)0x80;       //set aside the sign
       if (sign != 0) pcm_vals[i] = -pcm_vals[i];  //get magnitude
       if (pcm_vals[i] > CLIP) pcm_vals[i] = CLIP;  //clip the magnitude

       //Convert from 16 bit linear to ulaw.
       pcm_vals[i] += BIAS;
       exponent  = exp_lut[(pcm_vals[i] >> 7) & 0xFF];
       mantissa  = (byte)(pcm_vals[i] >> (exponent + 3)) & (byte)0x0F;
       ulaw_vals[i]   = (byte)(~(sign | (exponent << 4) | mantissa));
      }
    }

---------------
I suspect that your problem was the line
 if (sign != 0) pcm_vals[i] = -pcm_vals[i];  //get magnitude
because of the fact that you didn't have the sign correctly.
I didn't compiled this so maybe you will have to play a bit :-)
Hope this will work.

0
 
gadioCommented:
(I assumed that pcm_vals is a short[] type)
0
 
BonevCommented:
Roy, the problem is with that conversion. To make your life easier you could use this:

ByteArrayInputStream is = new ByteArrayInputStream(b_arr);
DataInputStream ds = new DataInputStream(is);
int[] i_arr = new int[numOfInts];
for (int j=0; j<numOfInts; j++)
{
   i_arr[j] = ds.readShort();
}

0
 
theGrayPilgrimAuthor Commented:
gadio and Bonev,

On both cases, no success.
Bonev, when I tried your code, I got just random noises.
gadio, with your code, the quality is still as poor as ever.

Since it is getting late out here, I will come back tomorrow and try both your solutions again, with a clearer head. If you come up with anything new, please let me know.

thanks for your time and efforts...
0
 
theGrayPilgrimAuthor Commented:
gadio , Bonev and diakov ,

I found and fixed the problem. As both gadio and Bonev pointed out, the problem was with the following line -

   i_arr[j++] = (int)(b_arr[i] | (b_arr[i+1]) << 8));

the byte values are promoted to 32 bit signed integers before the shift is performed. That means, that if the sign bit of the byte is 1, all bits left from it will be set to 1.

Both the solutions you sent me last didn't work (if you would like to have a look at the solution that did work, I added it at the end).

Since you both put me on the right track, I will be happy to give each 150pts. I am not sure how this can be done. I suggest that one of you send a proposed answer to this question, which I will give 150pts for, and I will post a new 150pts question for the other.
Let me know if this is acceptable to you.

(diakov, thanks for your help...)

Here is the good code - b_arr is the array of bytes, and i_arr is the array of integers that I later send for conversion.

for (int i=0; i<numOfInts; i++)
{
  if (b_arr[i*2+1] < 0)
    i_arr[i] = (0xffffff00 ^ b_arr[i*2+1]) << 8;
  else
    i_arr[i] = b_arr[i*2+1] << 8;

  if (b_arr[i*2] < 0)
    i_arr[i] |= (0xffffff00 ^ b_arr[i*2]);
  else
    i_arr[i] |= b_arr[i*2];
}

0
 
diakovCommented:
Hi 'theGrayPilgrim',

This is true that Java promotes the arguments of aritmetic operations to int (or if higher bitwidth leaves them as they are).
I had to make C and Java programs in order to equalize the output and achieve what you did.

Success.
0
 
gadioCommented:
Roy hi. I think thats fine. I will lock the question, but DONT accept it still. First issue a request to the customer support area to reduce the points of this question to 150, and only then accept it. Next, issue a 'dummy' question to Bonev of 150 points.

Glad we help you, regards,

G.
0
 
gadioCommented:
I'll lock the Q now...
0
 
BonevCommented:
Roy, thanks.

BTW, I believe it is easier to reduce the points by rejecting the proposed answer and then changing the number of points.
Of course, gadjo will have to answer again.

0
 
gadioCommented:
Sure thing. But I thought that reducing is not possible is it? Its been a while since I asked a Q...  ;-)
0
 
BonevCommented:
Sorry, it is not :(
0
 
theGrayPilgrimAuthor Commented:
Earlier today I posted a request to the support team to reduce the point for the question to 150. I Also think, like gadio, that reducing the point is not allowed.
If nothing happens within the next couple of days, we'll think of something else.


0
 
theGrayPilgrimAuthor Commented:
Bonev & gadio,

A question for each of you was posted by Linda.

Thanks again for your help...
0
 
linda101698Commented:
I'm posting an answer so this question can be saved in the previously asked questions.

Linda Gardner
Customer Service @ Experts Exchange
0
 
glynpCommented:
It looks like a way of doing what I want to do - ie. record messages under win 95 to play back in a java applet. I expected to find some very simple utility to do it. Alas the 2 I found, one wouldn't cope with the WAV files (sox) the other blew my machine apart with a kernel32 problem.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

  • 9
  • 7
  • 6
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now