In article <e8**********@scotsman.ed.ac.uk>
Robert Dow <rj*@music.ed.ac.ukwrote:
>... I deal a lot with sound data which comes in a fixed format
of either signed 16 or 24 bit integers. What is the best way of
defining these types to allow some level of portability?
First, you need to decide "how much portability", because:
- most machines today have signed, 16-bit, two's complement "short",
so you can just use that for the first;
- any system that supports "enough" C99 will also have <inttypes.h>
which will define the type "int16_t" for you; and
- it is possible to do all your 16-bit work with "unsigned short"
(which is guaranteed to be at least 16 bits) or plain (signed)
long in such a way that it is 100% portable, i.e., will work on
*any* conforming C implementation, even if it has ones' complement
signed arithmetic and/or 18- or 64-bit "short" -- but it will
take more work.
Having made such a decision...
>Also, for converting 24 bit integers to 32 bit (so I can use them) I have
written this:
#define WORD24_LEN 3
typedef long s32word_t; /* OK for Max OS X/G5 but not nesc. other platforms */
union word32 {
s32word_t lword;
unsigned char byte[4];
};
(and complicated code to manipulate them)
The phrases that come to mind here are "ew" and "don't do that". :-)
Given 3 8-bit octets that should from a "FILE *fp", do this -- note
that you can do the obvious thing if these octet-bytes are already
in an array of "unsigned char":
unsigned long accum;
long val;
unsigned char c[3];
/* fread takes (base, width, nelements, FILE *) */
if (fread(c, 1, 3, fp) != 3) ... handle error ...
#if UCHAR_MAX != 255 /* this #if is optional */
/*
* This step is optional, and a no-op if UCHAR_MAX is 255 as usual;
* or you could inspect the values to see if they are out of range,
* which implies something went wrong in reading 8-bit octets on
* your 9- or 18- or 24-bit "unsigned char" machine.
*
* Note that a good C compiler should generate no code here even
* without the "#if" above, in the usual (UCHAR_MAX == 255) case.
*/
c[0] &= 0xff;
c[1] &= 0xff;
c[2] &= 0xff;
#endif
/* here is where you decide on File Byte Order */
accum = (unsigned long)c[0] << 16; /* if c0 is the topmost bits */
accum |= (unsigned)c[1] << 8;
accum |= c[2];
#define SIGNBIT (1UL << 23)
/* now convert unsigned 24-bit value to signed 32-bit value */
val = (long)(accum ^ SIGNBIT) - (long)SIGNBIT;
Note that this code can be adapted to force the correct interpretation
of any bit-length word up to 31, or in C99, 63 using "long long".
This code works even if the target machine has 72-bit "int" or uses
ones' complement, because it operates strictly on the *values*
in the 24-bit field.
You can see how this works if we make a table for a 3-bit two's
complement number, which can go from -4 to +3:
desired value (as unsigned) value after
"sign" bit1 bit0 value after flipping "sign" subtracting 4
------ ---- ---- ---- --------------------- -------------
0 0 0 0 4 0
0 0 1 1 5 1
0 1 0 2 6 2
0 1 1 3 7 3
1 0 0 -4 0 -4
1 0 1 -3 1 -3
1 1 0 -2 2 -2
1 1 1 -1 3 -1
The only gimmick here is that the intermediate and final signed
values (after flipping the top bit and converting to the signed
variant) have to fit within LONG_MIN..LONG_MAX. Since these are
guaranteed to be at least -2147483647..+2147483647, we can certainly
go to 31 bits, which needs only a range of -1073741824..+1073741823.
(Going to 32 fails if LONG_MIN is -2147483647 instead of -2147483648,
but otherwise actually still works.)
To convert back, use the obvious corresponding technique (add bias
with native signed arithmetic, convert to "unsigned long", and flip
top bit).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.