0

I got a very strange alignment exception, which only occurs on certain hardware combinations. I have implemented a bluetooth audio sink, which get its data fed from a unix file descriptor. When i combine a Macbook Pro (as bluetooth source) and a raspberry pi (as bluetooth sink), i get an alignment exception at the following point:

void process(uint8_t* inData, uint32_t size, float* outData)
{
    int16_t* from = (int16_t*)inData;
    float* to = outData;

    for (size_t i = 0; i < size/2; ++i) {
        *to = *from/32767.0;
        ++to;
        ++from; // Crashes on MacbookPro/RasPi combination
    }
}

How comes? My sink obviously does not know about my source. And this works for other platforms (combinations)?

I also tried this snippet, however, also no success.

int8_t* from = (int8_t*)inData;
float* to = outData;

for (size_t i = 0; i < size/2; ++i) {
    int16_t tmp;
    std::memcpy(&tmp, from, 2);
    *to = tmp/32767.0;
    ++to;
    from += 2; // This crashes
}

I guess a running example would not help here, since the exact same code works, when using another data (bluetooth) source.

mincequi
  • 55
  • 5
  • 1
    Make sure the `inData` pointer has proper `int16_t` alignment for what it points to. – Eljay Dec 01 '19 at 17:08
  • I am using the sbc decoder lib from https://github.com/heinervdm/bluez/blob/master/sbc/sbc.c Data is clearly aligned as int16_t: int16_t s; // = .... char* ptr; // = ... if (sbc->endian == SBC_BE) { *ptr++ = (s & 0xff00) >> 8; *ptr++ = (s & 0x00ff); } else { *ptr++ = (s & 0x00ff); *ptr++ = (s & 0xff00) >> 8; } – mincequi Dec 01 '19 at 17:49

2 Answers2

2

You are treating a pointer to an 8-bit value as a pointer to a 16-bit value. This is undefined.

Further, typically, 8-bit values will have an alignment of 1 byte, where as a 16-bit value has am alignment of 2 bytes.

As Eljay said in the comments, you can change the alignment of inData, but the result is still not defined.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48
  • I thought char pointers (unspecified, unsigned, signed, including `int8_t*`) were exempted from the aliasing disallowed rule. – Eljay Dec 01 '19 at 17:22
  • 1
    See [here](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). The last part of the first question specifies types you can use. `char` is one, but I don't know that it extends to `uint_8` – ChrisMM Dec 01 '19 at 17:28
0

This fixed it:

    char* to; // = ...
    char* from; // = ...

    for (size_t i = 0; i < buffer.size()/size(conf.codec); ++i) {
        int16_t tmp;
        std::memcpy(&tmp, from+(i*2), 2);
        float f = tmp/32767.0;
        std::memcpy(to+(i*4), &f, 4);
    }
mincequi
  • 55
  • 5
  • `memcpy` is from a byte buffer into the right type (that has the correct alignment) is one good way of handling the issue. It can run afoul of endian issues, but as long as the data is all on the same platform that shouldn't be an issue. – Eljay Dec 01 '19 at 21:18