简体   繁体   中英

24 bit audio processing

I have written some code that read data from a audio buffer based on bit depth, processes it and writes it back to same buffer. I have to handle 16 and 24 bit depths. code for 16 bit works fine but for 24 bit it does not.

Here is what I am doing for each sample for 24 bit data:

int data = 0;

//read data from buffer
RtlCopyMemory (&data, ((char*)Source) + head, 3);

//scale data for integer

data = data << 8;

//data processing

data = data * m_CurOutVolLevel;

//scaling down the data
data = (data >> 8);

RtlCopyMemory (((char*)Source) + head, &data, 3);

but the output is not proper. Can someone point what's wrong here??

Thanks

Thanks for the suggestions. I implemented what you guys suggested but still there is some flaw:

unsigned __int64 newData;
unsigned char* s = ((unsigned char*)Source) + head;
unsigned int data = ((unsigned int)s[0] << 8) | ((unsigned int)s[1] << 16) |((unsigned int)s[2] << 24);

// data processing
newData = (unsigned __int64)(data * m_pMiniport->m_CurOutVolLevel);
//divide this by 2 to the power 16
newData = (newData >> 16); 

// store back
s[0] = (unsigned char)((newData >> 32)  & 0xff);
s[1] = (unsigned char)((newData >> 40) & 0xff);
s[2] = (unsigned char)((newData >> 48) & 0xff);

does anyone see problem with above code?

Thanks, Annie

Well, you use an int , if it is on a 32-bit machine this will typically be 32 bits. If data is 24 bits, shifting it will mean you can even use the highest order bit. This results in two things:

  • A first possible overload from the shift, as your int is signed, if bit "24" (highest order bit in your data) is 1, bit 32 in the result will be 1 and it will become a negative number.

  • A second possible overflow from the multiplication, suppose the above doesn't happen, it is very likely that you now will overgrow the boundaries of a 32 bit number. Suppose bit 23 is set, so your number stays possitive, if m_curOutVolLevel is even only as low as 2, this is equal to an exta bit shift and again you're overflowing. Much more likely you'll even lose bits because the number can't be stored in 32 bits anymore.

Edit: possible solution, if this is the problem: use uint64_t from stdint.h

Edit 2 : Also note that your code can give problem with endianness as you simply encode the int into a byte stream (unless RtlCopyMemory handles this, which I doubt).

Generalizing @user786653 answer for multiple bit depths.

uint32_t sample = 0;
for (int i = 0; i < bit_depth / 8; i++) {
    sample = ((uint32_t)source[i] << (i * 8)) | sample;
}

You can't modify parts of an int and just expect the code to work (let alone be portable).

Assuming the data is stored in unsigned little-endian order you can access it like this:

unsigned char* s = ((unsigned char*)Source) + head;
uint32_t data = ((uint32_t)s[0] << 8) | ((uint32_t)s[1] << 16) | ((uint32_t)s[2] << 24);
// use data
// store back
s[0] = (unsigned char)((data >> 8) & 0xff);
s[1] = (unsigned char)((data >> 16) & 0xff);
s[2] = (unsigned char)((data >> 24) & 0xff);

But this will depend on how the data is stored.

You need to be very careful about endianness issues with code like this, byte-copying into a multi-byte single variable is not safe.

It's better to read one byte at a time, so that you can express in endianness-proof way where in the value you expect each byte to land. This, of course, requires you to think about the byte-ordering of the multi-byte 24-bit values in the source data, instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM