Depending on the requirements I'd use a bitfield for it.
struct int24{
unsigned int data : 24;
};
Or, if a separation is easier, just use 3 bytes (chars).
Btw, both use cases you mention in the question generally use 32bit integers. In the case of audio processing you'll generally convert to 32 bit ints (or floats, preferably, to prevent overflow situations you'd get with fixed point or integer math) when loading in chunks of audio because you're not going to have the entire file in memory at once.
For image data, people just tend to use 32 bit integers and ignore the alpha 8 alpha bits all together, or if you're dealing with a tightly packed format you're probably better of just manipulating them as char-pointers anyway because you'll have all channels separate. It's going to be a performance/memory trade-off anyway because writing one int is generally faster than three chars separately; however it will take 25% more memory.
Packing structs like this is compiler specific. However, in Visual Studio you'd do the following to make the struct exactly 24 bits.
#pragma pack(push, 1)
struct int24{
unsigned int data : 24;
}
#pragma pack(pop)