From 1d6beece31584c71a7ce730133923f2e1d0d3b7d Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Sat, 24 Mar 2007 02:56:52 +0000 Subject: add Marcel's original, unmodified sbcdec/enc clients to sbc --- sbc/sbcdec.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++- sbc/sbcenc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 417 insertions(+), 2 deletions(-) (limited to 'sbc') diff --git a/sbc/sbcdec.c b/sbc/sbcdec.c index 208cdfd8..0617fe04 100644 --- a/sbc/sbcdec.c +++ b/sbc/sbcdec.c @@ -1,6 +1,6 @@ /* * - * Bluetooth low-complexity, subband codec (SBC) library + * Bluetooth low-complexity, subband codec (SBC) decoder * * Copyright (C) 2004-2007 Marcel Holtmann * @@ -25,9 +25,202 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "sbc.h" +#define BUF_SIZE 8192 + +static void decode(char *filename, char *audiodevice, int tofile) +{ + unsigned char buf[BUF_SIZE], *stream; + struct stat st; + off_t filesize; + sbc_t sbc; + int fd, ad, pos, streamlen, framelen, count, format = AFMT_S16_BE; + + if (stat(filename, &st) < 0) { + fprintf(stderr, "Can't get size of file %s: %s\n", + filename, strerror(errno)); + return; + } + + filesize = st.st_size; + stream = malloc(st.st_size); + + if (!stream) { + fprintf(stderr, "Can't allocate memory for %s: %s\n", + filename, strerror(errno)); + return; + } + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Can't open file %s: %s\n", + filename, strerror(errno)); + goto free; + } + + if (read(fd, stream, st.st_size) != st.st_size) { + fprintf(stderr, "Can't read content of %s: %s\n", + filename, strerror(errno)); + close(fd); + goto free; + } + + close(fd); + + pos = 0; + streamlen = st.st_size; + + ad = open(audiodevice, O_WRONLY | (tofile ? (O_CREAT | O_TRUNC) : 0), tofile ? 0644 : 0); + if (ad < 0) { + fprintf(stderr, "Can't open audio device %s: %s\n", + audiodevice, strerror(errno)); + goto free; + } + + sbc_init(&sbc, 0L); + + framelen = sbc_decode(&sbc, stream, streamlen); + printf("%d Hz, %d channels\n", sbc.rate, sbc.channels); + if (!tofile) { + if (ioctl(ad, SNDCTL_DSP_SETFMT, &format) < 0) { + fprintf(stderr, "Can't set audio format on %s: %s\n", + audiodevice, strerror(errno)); + goto close; + } + if (ioctl(ad, SNDCTL_DSP_CHANNELS, &sbc.channels) < 0) { + fprintf(stderr, "Can't set number of channels on %s: %s\n", + audiodevice, strerror(errno)); + goto close; + } + + if (ioctl(ad, SNDCTL_DSP_SPEED, &sbc.rate) < 0) { + fprintf(stderr, "Can't set audio rate on %s: %s\n", + audiodevice, strerror(errno)); + goto close; + } + } + + count = 0; + while (framelen > 0) { + // we have completed an sbc_decode at this point + // sbc.len is the length of the frame we just decoded + // count is the number of decoded bytes yet to be written + + if (count + sbc.len > BUF_SIZE) { + // buffer is too full to stuff decoded audio in + // so it must be written to the device + write(ad, buf, count); + count = 0; + } + + // sanity check + if(count + sbc.len > BUF_SIZE) { + fprintf(stderr, "buffer size of %d is too small for decoded data (%d)\n", BUF_SIZE, sbc.len + count); + exit(1); + } + + // move the latest decoded data into buf and increase the count + memcpy(buf + count, sbc.data, sbc.len); + count += sbc.len; + + // push the pointer in the file forward to the next bit to be decoded + // tell the decoder to decode up to the remaining length of the file (!) + pos += framelen; + framelen = sbc_decode(&sbc, stream + pos, streamlen - pos); + } + + if (count > 0) + write(ad, buf, count); + +close: + sbc_finish(&sbc); + + close(ad); + +free: + free(stream); +} + +static void usage(void) +{ + printf("SBC decoder utility ver %s\n", VERSION); + printf("Copyright (c) 2004 Marcel Holtmann\n\n"); + + printf("Usage:\n" + "\tsbcdec [options] file(s)\n" + "\n"); + + printf("Options:\n" + "\t-h, --help Display help\n" + "\t-d, --device Sound device\n" + "\t-v, --verbose Verbose mode\n" + "\t-f, --file Decode to a file\n" + "\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'd' }, + { "verbose", 0, 0, 'v' }, + { "file", 1, 0, 'f' }, + { 0, 0, 0, 0 } +}; + int main(int argc, char *argv[]) { + char *device = NULL; + char *file = NULL; + int i, opt, verbose = 0, tofile = 0; + + while ((opt = getopt_long(argc, argv, "+hd:vf:", main_options, NULL)) != -1) { + switch(opt) { + case 'h': + usage(); + exit(0); + + case 'd': + device = strdup(optarg); + break; + + case 'v': + verbose = 1; + break; + case 'f' : + file = strdup(optarg); + tofile = 1; + break; + + default: + exit(1); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + for (i = 0; i < argc; i++) + decode(argv[i], device ? device : file ? file : "/dev/dsp", tofile); + + if (device) + free(device); + return 0; } diff --git a/sbc/sbcenc.c b/sbc/sbcenc.c index 208cdfd8..8919fb92 100644 --- a/sbc/sbcenc.c +++ b/sbc/sbcenc.c @@ -1,6 +1,6 @@ /* * - * Bluetooth low-complexity, subband codec (SBC) library + * Bluetooth low-complexity, subband codec (SBC) encoder * * Copyright (C) 2004-2007 Marcel Holtmann * @@ -25,9 +25,231 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "sbc.h" +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) +#define LE_SHORT(v) (v) +#define LE_INT(v) (v) +#define BE_SHORT(v) bswap_16(v) +#define BE_INT(v) bswap_32(v) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) +#define LE_SHORT(v) bswap_16(v) +#define LE_INT(v) bswap_32(v) +#define BE_SHORT(v) (v) +#define BE_INT(v) (v) +#else +#error "Wrong endian" +#endif + +#define AU_MAGIC COMPOSE_ID('.','s','n','d') + +#define AU_FMT_ULAW 1 +#define AU_FMT_LIN8 2 +#define AU_FMT_LIN16 3 + +struct au_header { + uint32_t magic; /* '.snd' */ + uint32_t hdr_size; /* size of header (min 24) */ + uint32_t data_size; /* size of data */ + uint32_t encoding; /* see to AU_FMT_XXXX */ + uint32_t sample_rate; /* sample rate */ + uint32_t channels; /* number of channels (voices) */ +}; + +static ssize_t __read(int fd, void *buf, size_t count) +{ + ssize_t len, pos = 0; + + while (count > 0) { + len = read(fd, buf + pos, count); + if (len <= 0) + return len; + + count -= len; + pos += len; + } + + return pos; +} + +static ssize_t __write(int fd, const void *buf, size_t count) +{ + ssize_t len, pos = 0; + + while (count > 0) { + len = write(fd, buf + pos, count); + if (len <= 0) + return len; + + count -= len; + pos += len; + } + + return pos; +} + +static void encode(char *filename, int subbands, int joint) +{ + struct au_header *au_hdr; + unsigned char buf[2048]; + sbc_t sbc; + int fd, len, size, count; + + if (strcmp(filename, "-")) { + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Can't open file %s: %s\n", + filename, strerror(errno)); + return; + } + } else + fd = fileno(stdin); + + len = __read(fd, buf, sizeof(buf)); + if (len < sizeof(*au_hdr)) { + if (fd > fileno(stderr)) + fprintf(stderr, "Can't read header from file %s: %s\n", + filename, strerror(errno)); + else + perror("Can't read audio header"); + goto done; + } + + au_hdr = (struct au_header *) buf; + + if (au_hdr->magic != AU_MAGIC || + BE_INT(au_hdr->hdr_size) > 128 || + BE_INT(au_hdr->hdr_size) < 24 || + BE_INT(au_hdr->encoding) != AU_FMT_LIN16) { + fprintf(stderr, "Data is not in Sun/NeXT audio S16_BE format\n"); + goto done; + } + + sbc_init(&sbc, 0L); + + sbc.rate = BE_INT(au_hdr->sample_rate); + sbc.channels = BE_INT(au_hdr->channels); + sbc.subbands = subbands; + sbc.joint = joint; + count = BE_INT(au_hdr->data_size); + size = len - BE_INT(au_hdr->hdr_size); + memmove(buf, buf + BE_INT(au_hdr->hdr_size), size); + + while (1) { + if (size < sizeof(buf)) { + len = __read(fd, buf + size, sizeof(buf) - size); + if (len == 0) + break; + + if (len < 0) { + perror("Can't read audio data"); + break; + } + + size += len; + } + + len = sbc_encode(&sbc, buf, size); + if (len < size) + memmove(buf, buf + len, size - len); + + size -= len; + + len = __write(fileno(stdout), sbc.data, sbc.len); + if (len == 0) + break; + + if (len < 0 || len != sbc.len) { + perror("Can't write SBC output"); + break; + } + } + + sbc_finish(&sbc); + +done: + if (fd > fileno(stderr)) + close(fd); +} + +static void usage(void) +{ + printf("SBC encoder utility ver %s\n", VERSION); + printf("Copyright (c) 2004 Marcel Holtmann\n\n"); + + printf("Usage:\n" + "\tsbcenc [options] file(s)\n" + "\n"); + + printf("Options:\n" + "\t-h, --help Display help\n" + "\t-v, --verbose Verbose mode\n" + "\t-s, --subbands Number of subbands to use (4 or 8)\n" + "\t-j, --joint Joint stereo\n" + "\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "verbose", 0, 0, 'v' }, + { "subbands", 1, 0, 's' }, + { "joint", 0, 0, 'j' }, + { 0, 0, 0, 0 } +}; + int main(int argc, char *argv[]) { + int i, opt, verbose = 0, subbands = 8, joint = 0; + + while ((opt = getopt_long(argc, argv, "+hv", main_options, NULL)) != -1) { + switch(opt) { + case 'h': + usage(); + exit(0); + + case 'v': + verbose = 1; + break; + case 's': + subbands = atoi(strdup(optarg)); + if (subbands != 8 && subbands != 4) { + fprintf(stderr, "Invalid number of sunnabds %d!\n", subbands); + exit(1); + } + break; + case 'j': + joint = 1; + break; + + default: + exit(1); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + for (i = 0; i < argc; i++) + encode(argv[i], subbands, joint); + return 0; } -- cgit