Hi all. I've got some 16-bit PCM data, and I'm trying to add a level adjustment to my code. I thought it would be really straight forward, but it's causing a lot of distortion when I try to lower the level. It sounds bad most of the time if I increase the level, but I'm pretty sure that's just clipping. If the db_gain variables are set to 0, everything sounds fine. Here is the code: // in this case, db_gain_left and db_gain_right would have // values in the -12 to +12 range. int db_gain_left = -3, db_gain_right = -3; // calculate a multiplication factor from the gain double db_left_factor = pow(10.0, (db_gain_left/20.0)); double db_right_factor = pow(10.0, (db_gain_right/20.0)); // multiply by 2^16 to speed up conversion later // saves a step in the loop db_left_factor *= 65536.0; db_right_factor *= 65536.0; // make working with samples easier unsigned short *in, *out; in = (unsigned short*)pOutBuf; out = (unsigned short*)tempBuf; // samples are two byte shorts, so there are half // as many samples as bytes int num_samples = inputBytes / 2; float left = 0.0, right = 0.0; for(int z = 0; z < num_samples; z+=2) { // convert each sample to from PCM to float between 0 and 1 left = in[z] / 65536.0; right = in[z+1] / 65536.0; // multiply each sample by the corrected factor left *= db_left_factor; right *= db_right_factor; // check for clipping if(left > 0xFFFF) left = 0xFFFF; if(right > 0xFFFF) right = 0xFFFF; // put values in output out[z] = (int)left; out[z+1] = (int)right; } Anyone see anything obviouos I'm doing wrong? The way I see it, for example, a sample might have the values: left = 48500, right = 23450 the gain is -3 for left and right db_factor_left = db_factor_right = 0.7079 multiply this by 65536 now, or do it later, shouldn't matter db_factor_left = db_factor_right = 46395.9394 convert to floating point by deviding each by 65536 left = 0.7401, right = 0.3578 multiply by the db_factors left = 34337.6314, right = 16600.4655 which are values scaled about how you would think based on the change. I really don't see where the math is wrong, but for some reason, it sounds bad. How else do you adjust volume levels? Thanks for reading. -james

# Why is my level adjustment distorting the output?

Started by ●December 16, 2005

Reply by ●December 16, 20052005-12-16

Some things to check - Your 16-bit PCM may be [-2^15, 2^15-1] rather than [0, 2^16-1], if so, use signed, not unsigned, with appropriate changes. (You want to scale relative to 0, not relative to the most negative value.) How is 0xFFFF being interpreted by your compiler? If it is considered 16-bit signed, then it's decimal value is -1, not the max int value you are expecting. You may be half-wave rectifying your signal. Check. You are only checking the clipping in the positive direction, what about negative? Most of all: Figure out how your input data is represented, then put a synthesized sine wave into this code and look at the output. Debug your code. Dirk spagthorpe@gmail.com wrote:> Hi all. > > I've got some 16-bit PCM data, and I'm trying to add a level > adjustment to my code. I thought it would be really straight forward, > but it's causing a lot of distortion when I try to lower the level. It > sounds bad most of the time if I increase the level, but I'm pretty > sure that's just clipping. If the db_gain variables are set to 0, > everything sounds fine. > > Here is the code: > > // in this case, db_gain_left and db_gain_right would have > // values in the -12 to +12 range. > int db_gain_left = -3, db_gain_right = -3; > > // calculate a multiplication factor from the gain > double db_left_factor = pow(10.0, (db_gain_left/20.0)); > double db_right_factor = pow(10.0, (db_gain_right/20.0)); > > // multiply by 2^16 to speed up conversion later > // saves a step in the loop > db_left_factor *= 65536.0; > db_right_factor *= 65536.0; > > // make working with samples easier > unsigned short *in, *out; > in = (unsigned short*)pOutBuf; > out = (unsigned short*)tempBuf; > > // samples are two byte shorts, so there are half > // as many samples as bytes > int num_samples = inputBytes / 2; > > float left = 0.0, right = 0.0; > > for(int z = 0; z < num_samples; z+=2) { > > // convert each sample to from PCM to float between 0 and 1 > left = in[z] / 65536.0; > right = in[z+1] / 65536.0; > > // multiply each sample by the corrected factor > left *= db_left_factor; > right *= db_right_factor; > > // check for clipping > if(left > 0xFFFF) left = 0xFFFF; > if(right > 0xFFFF) right = 0xFFFF; > > // put values in output > out[z] = (int)left; > out[z+1] = (int)right; > } > > Anyone see anything obviouos I'm doing wrong? > > The way I see it, for example, a sample might have the values: > > left = 48500, right = 23450 > > the gain is -3 for left and right > db_factor_left = db_factor_right = 0.7079 > > multiply this by 65536 now, or do it later, shouldn't matter > db_factor_left = db_factor_right = 46395.9394 > > convert to floating point by deviding each by 65536 > > left = 0.7401, right = 0.3578 > > multiply by the db_factors > > left = 34337.6314, right = 16600.4655 > > which are values scaled about how you would think based on the change. > > I really don't see where the math is wrong, but for some reason, it > sounds bad. > > How else do you adjust volume levels? > > Thanks for reading. > > -james

Reply by ●December 16, 20052005-12-16