/* vmconvert.c - adapted by Peter Fokker ; deal with endianness 2005-08-03 */ #include #include #include "vmconvert.h" #include "gsm.h" static const struct riff_header { char szriff[4]; long filesize; char szwave[4]; char szfmt[4]; long fmthsize; short format; short channels; long rate; long bsec; short align; short bits; char szdata[4]; long datasize; } rh_init = { "RIFF", 0, "WAVE", "fmt ", 16, /* length of header */ 1, /* PCM */ 1, /* mono */ 8000, /* 8 kHz */ 16000, 2, /* bytes/sample */ 16, /* bits/sample */ "data", 0 }; static gsm r; int write_riff_header(void *buffer, int vmo_file_size) { int wav_data_size; struct riff_header *rh; wav_data_size = (vmo_file_size / 34) * 320; rh = (struct riff_header *)buffer; *rh = rh_init; rh->filesize = sizeof(struct riff_header) + wav_data_size - 8; rh->datasize = wav_data_size; /* take care of endian anomaly; make it little endian */ rh->filesize = endian_l(rh->filesize); rh->fmthsize = endian_l(rh->fmthsize); rh->format = endian_s(rh->format); rh->channels = endian_s(rh->channels); rh->rate = endian_l(rh->rate); rh->bsec = endian_l(rh->bsec); rh->align = endian_s(rh->align); rh->bits = endian_s(rh->bits); rh->datasize = endian_l(rh->datasize); return sizeof(struct riff_header); } int vmo_start() { if (!(r = gsm_create())) { perror("gsm_create"); return -1; } (void)gsm_option(r, GSM_OPT_FAST, 0); (void)gsm_option(r, GSM_OPT_VERBOSE, 0); return 0; } void vmo_decode(void *vmo_frame, void *buffer) { gsm_frame s; gsm_byte *p; gsm_signal *w; /* used to step output for endianness */ int i; p = (gsm_byte *)vmo_frame; for (i=0; i<32; i+=2) { s[i] = *p; s[i+1] = *(p+3); p += 2; } s[i] = *p; s[0] &= 0x0f; s[0] |= 0xd0; if (gsm_decode(r, s, (gsm_signal *)buffer)) { /* invalid frame */ bzero(buffer, 160 * sizeof(gsm_signal)); } else { /* valid frame, but how about endianness? */ for (w = (gsm_signal *) buffer, i=160; (i-- > 0); ++w) { *w = endian_s(*w); } } return; } void vmo_end() { gsm_destroy(r); } /* These two routines convert shorts (16 bits) and * longs (32 bits) to little endian format. Trick is to let the * compiler store a multibyte number in endian_test. On a little * endian machine, this will result in the LSB being 1 whereas * on a big endian machine it will be 0. By casting a pointer * to unsigned char, we can determine the endianness automagically. * If we discover we are running on a big endian, we swap, otherwise * we do nothing. */ short endian_s(short i) { static short endian_test = 0x0001; unsigned char *little_endian = (unsigned char *) &endian_test; unsigned char b0,b1; if (*little_endian) { return i; } else { b0 = (i & 0x00FF); b1 = ((i & 0xFF00) >> 8); return (b0 << 8) | b1; } } long endian_l(long i) { static short endian_test = 0x0001; unsigned char *little_endian = (unsigned char *) &endian_test; unsigned char b0,b1,b2,b3; if (*little_endian) { return i; } else { b0 = (i & 0x000000FF); b1 = ((i & 0x0000FF00) >> 8); b2 = ((i & 0x00FF0000) >> 16); b3 = ((i & 0xFF000000) >> 24); return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; } }