Solved

Audio formats conversion bug

Posted on 1998-10-13
30
335 Views
Last Modified: 2008-02-01
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
Comment
Question by:theGrayPilgrim
  • 9
  • 7
  • 6
  • +3
30 Comments
 
LVL 8

Expert Comment

by:diakov
ID: 1225700
How about shifting the sign to << with 7 before the |?
0
 

Author Comment

by:theGrayPilgrim
ID: 1225701
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
 
LVL 8

Expert Comment

by:diakov
ID: 1225702
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
 

Author Comment

by:theGrayPilgrim
ID: 1225703
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
 
LVL 1

Expert Comment

by:Bonev
ID: 1225704
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
 

Author Comment

by:theGrayPilgrim
ID: 1225705
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
 
LVL 8

Expert Comment

by:diakov
ID: 1225706
'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
 
LVL 8

Expert Comment

by:diakov
ID: 1225707
I think I pasted some lines twice, sorry.
0
 
LVL 1

Expert Comment

by:Bonev
ID: 1225708
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
 
LVL 1

Expert Comment

by:Bonev
ID: 1225709
I mean 8 vs. 16 bits
0
 
LVL 8

Expert Comment

by:diakov
ID: 1225710
just in case. :-)

(I going out)
0
 

Author Comment

by:theGrayPilgrim
ID: 1225711
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
 
LVL 1

Expert Comment

by:Bonev
ID: 1225712
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
 

Author Comment

by:theGrayPilgrim
ID: 1225713
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
 
LVL 6

Expert Comment

by:gadio
ID: 1225714
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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 6

Expert Comment

by:gadio
ID: 1225715
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
 
LVL 6

Expert Comment

by:gadio
ID: 1225716
(I assumed that pcm_vals is a short[] type)
0
 
LVL 1

Expert Comment

by:Bonev
ID: 1225717
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
 

Author Comment

by:theGrayPilgrim
ID: 1225718
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
 

Author Comment

by:theGrayPilgrim
ID: 1225719
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
 
LVL 8

Expert Comment

by:diakov
ID: 1225720
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
 
LVL 6

Expert Comment

by:gadio
ID: 1225721
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
 
LVL 6

Expert Comment

by:gadio
ID: 1225722
I'll lock the Q now...
0
 
LVL 1

Expert Comment

by:Bonev
ID: 1225723
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
 
LVL 6

Expert Comment

by:gadio
ID: 1225724
Sure thing. But I thought that reducing is not possible is it? Its been a while since I asked a Q...  ;-)
0
 
LVL 1

Expert Comment

by:Bonev
ID: 1225725
Sorry, it is not :(
0
 

Author Comment

by:theGrayPilgrim
ID: 1225726
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
 

Author Comment

by:theGrayPilgrim
ID: 1225727
Bonev & gadio,

A question for each of you was posted by Linda.

Thanks again for your help...
0
 
LVL 7

Accepted Solution

by:
linda101698 earned 350 total points
ID: 1225728
I'm posting an answer so this question can be saved in the previously asked questions.

Linda Gardner
Customer Service @ Experts Exchange
0
 

Expert Comment

by:glynp
ID: 1225729
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

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

For customizing the look of your lightweight component and making it look opaque like it was made of plastic.  This tip assumes your component to be of rectangular shape and completely opaque.   (CODE)
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Viewers will learn about basic arrays, how to declare them, and how to use them. Introduction and definition: Declare an array and cover the syntax of declaring them: Initialize every index in the created array: Example/Features of a basic arr…
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now