From 91815a03cf63a859a5bf5a46c3d5d40337853767 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jul 2012 20:00:29 -0700 Subject: src: Move subband encoder, decoder and info tools --- sbc/formats.h | 55 ---------- sbc/sbcdec.c | 293 ----------------------------------------------------- sbc/sbcenc.c | 308 ------------------------------------------------------- sbc/sbcinfo.c | 321 ---------------------------------------------------------- src/formats.h | 55 ++++++++++ src/sbcdec.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sbcenc.c | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sbcinfo.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 977 insertions(+), 977 deletions(-) delete mode 100644 sbc/formats.h delete mode 100644 sbc/sbcdec.c delete mode 100644 sbc/sbcenc.c delete mode 100644 sbc/sbcinfo.c create mode 100644 src/formats.h create mode 100644 src/sbcdec.c create mode 100644 src/sbcenc.c create mode 100644 src/sbcinfo.c diff --git a/sbc/formats.h b/sbc/formats.h deleted file mode 100644 index 3050b25..0000000 --- a/sbc/formats.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * Bluetooth low-complexity, subband codec (SBC) library - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include - -#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) */ -}; diff --git a/sbc/sbcdec.c b/sbc/sbcdec.c deleted file mode 100644 index 7008e88..0000000 --- a/sbc/sbcdec.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * - * Bluetooth low-complexity, subband codec (SBC) decoder - * - * Copyright (C) 2008-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sbc.h" -#include "formats.h" - -#define BUF_SIZE 8192 - -static int verbose = 0; - -static void decode(char *filename, char *output, int tofile) -{ - unsigned char buf[BUF_SIZE], *stream; - struct stat st; - sbc_t sbc; - int fd, ad, pos, streamlen, framelen, count; - size_t len; - int format = AFMT_S16_BE, frequency, channels; - ssize_t written; - - if (stat(filename, &st) < 0) { - fprintf(stderr, "Can't get size of file %s: %s\n", - filename, strerror(errno)); - return; - } - - 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; - - if (tofile) - ad = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644); - else - ad = open(output, O_WRONLY, 0); - - if (ad < 0) { - fprintf(stderr, "Can't open output %s: %s\n", - output, strerror(errno)); - goto free; - } - - sbc_init(&sbc, 0L); - sbc.endian = SBC_BE; - - framelen = sbc_decode(&sbc, stream, streamlen, buf, sizeof(buf), &len); - channels = sbc.mode == SBC_MODE_MONO ? 1 : 2; - switch (sbc.frequency) { - case SBC_FREQ_16000: - frequency = 16000; - break; - - case SBC_FREQ_32000: - frequency = 32000; - break; - - case SBC_FREQ_44100: - frequency = 44100; - break; - - case SBC_FREQ_48000: - frequency = 48000; - break; - default: - frequency = 0; - } - - if (verbose) { - fprintf(stderr,"decoding %s with rate %d, %d subbands, " - "%d bits, allocation method %s and mode %s\n", - filename, frequency, sbc.subbands * 4 + 4, sbc.bitpool, - sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS", - sbc.mode == SBC_MODE_MONO ? "MONO" : - sbc.mode == SBC_MODE_STEREO ? - "STEREO" : "JOINTSTEREO"); - } - - if (tofile) { - struct au_header au_hdr; - - au_hdr.magic = AU_MAGIC; - au_hdr.hdr_size = BE_INT(24); - au_hdr.data_size = BE_INT(0); - au_hdr.encoding = BE_INT(AU_FMT_LIN16); - au_hdr.sample_rate = BE_INT(frequency); - au_hdr.channels = BE_INT(channels); - - written = write(ad, &au_hdr, sizeof(au_hdr)); - if (written < (ssize_t) sizeof(au_hdr)) { - fprintf(stderr, "Failed to write header\n"); - goto close; - } - } else { - if (ioctl(ad, SNDCTL_DSP_SETFMT, &format) < 0) { - fprintf(stderr, "Can't set audio format on %s: %s\n", - output, strerror(errno)); - goto close; - } - - if (ioctl(ad, SNDCTL_DSP_CHANNELS, &channels) < 0) { - fprintf(stderr, "Can't set number of channels on %s: %s\n", - output, strerror(errno)); - goto close; - } - - if (ioctl(ad, SNDCTL_DSP_SPEED, &frequency) < 0) { - fprintf(stderr, "Can't set audio rate on %s: %s\n", - output, strerror(errno)); - goto close; - } - } - - count = len; - - 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 + len >= BUF_SIZE) { - /* buffer is too full to stuff decoded audio in so it - * must be written to the device */ - written = write(ad, buf, count); - if (written > 0) - count -= written; - } - - /* sanity check */ - if (count + len >= BUF_SIZE) { - fprintf(stderr, - "buffer size of %d is too small for decoded" - " data (%lu)\n", BUF_SIZE, (unsigned long) (len + count)); - exit(1); - } - - /* 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, - buf + count, sizeof(buf) - count, &len); - - /* increase the count */ - count += len; - } - - if (count > 0) { - written = write(ad, buf, count); - if (written > 0) - count -= written; - } - -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-2010 Marcel Holtmann\n\n"); - - printf("Usage:\n" - "\tsbcdec [options] file(s)\n" - "\n"); - - printf("Options:\n" - "\t-h, --help Display help\n" - "\t-v, --verbose Verbose mode\n" - "\t-d, --device Sound device\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 *output = NULL; - int i, opt, tofile = 0; - - while ((opt = getopt_long(argc, argv, "+hvd:f:", - main_options, NULL)) != -1) { - switch(opt) { - case 'h': - usage(); - exit(0); - - case 'v': - verbose = 1; - break; - - case 'd': - free(output); - output = strdup(optarg); - tofile = 0; - break; - - case 'f' : - free(output); - output = 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], output ? output : "/dev/dsp", tofile); - - free(output); - - return 0; -} diff --git a/sbc/sbcenc.c b/sbc/sbcenc.c deleted file mode 100644 index 3d3a7dc..0000000 --- a/sbc/sbcenc.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * - * Bluetooth low-complexity, subband codec (SBC) encoder - * - * Copyright (C) 2008-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sbc.h" -#include "formats.h" - -static int verbose = 0; - -#define BUF_SIZE 32768 -static unsigned char input[BUF_SIZE], output[BUF_SIZE + BUF_SIZE / 4]; - -static void encode(char *filename, int subbands, int bitpool, int joint, - int dualchannel, int snr, int blocks) -{ - struct au_header au_hdr; - sbc_t sbc; - int fd, size, srate, codesize, nframes; - ssize_t encoded; - ssize_t len; - - if (sizeof(au_hdr) != 24) { - /* Sanity check just in case */ - fprintf(stderr, "FIXME: sizeof(au_hdr) != 24\n"); - return; - } - - 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, &au_hdr, sizeof(au_hdr)); - if (len < (ssize_t) 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; - } - - if (au_hdr.magic != AU_MAGIC || - BE_INT(au_hdr.hdr_size) > 128 || - BE_INT(au_hdr.hdr_size) < sizeof(au_hdr) || - BE_INT(au_hdr.encoding) != AU_FMT_LIN16) { - fprintf(stderr, "Not in Sun/NeXT audio S16_BE format\n"); - goto done; - } - - sbc_init(&sbc, 0L); - - switch (BE_INT(au_hdr.sample_rate)) { - case 16000: - sbc.frequency = SBC_FREQ_16000; - break; - case 32000: - sbc.frequency = SBC_FREQ_32000; - break; - case 44100: - sbc.frequency = SBC_FREQ_44100; - break; - case 48000: - sbc.frequency = SBC_FREQ_48000; - break; - } - - srate = BE_INT(au_hdr.sample_rate); - - sbc.subbands = subbands == 4 ? SBC_SB_4 : SBC_SB_8; - - if (BE_INT(au_hdr.channels) == 1) { - sbc.mode = SBC_MODE_MONO; - if (joint || dualchannel) { - fprintf(stderr, "Audio is mono but joint or " - "dualchannel mode has been specified\n"); - goto done; - } - } else if (joint && !dualchannel) - sbc.mode = SBC_MODE_JOINT_STEREO; - else if (!joint && dualchannel) - sbc.mode = SBC_MODE_DUAL_CHANNEL; - else if (!joint && !dualchannel) - sbc.mode = SBC_MODE_STEREO; - else { - fprintf(stderr, "Both joint and dualchannel mode have been " - "specified\n"); - goto done; - } - - sbc.endian = SBC_BE; - /* Skip extra bytes of the header if any */ - if (read(fd, input, BE_INT(au_hdr.hdr_size) - len) < 0) - goto done; - - sbc.bitpool = bitpool; - sbc.allocation = snr ? SBC_AM_SNR : SBC_AM_LOUDNESS; - sbc.blocks = blocks == 4 ? SBC_BLK_4 : - blocks == 8 ? SBC_BLK_8 : - blocks == 12 ? SBC_BLK_12 : SBC_BLK_16; - - if (verbose) { - fprintf(stderr, "encoding %s with rate %d, %d blocks, " - "%d subbands, %d bits, allocation method %s, " - "and mode %s\n", - filename, srate, blocks, subbands, bitpool, - sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS", - sbc.mode == SBC_MODE_MONO ? "MONO" : - sbc.mode == SBC_MODE_STEREO ? - "STEREO" : "JOINTSTEREO"); - } - - codesize = sbc_get_codesize(&sbc); - nframes = sizeof(input) / codesize; - while (1) { - unsigned char *inp, *outp; - /* read data for up to 'nframes' frames of input data */ - size = read(fd, input, codesize * nframes); - if (size < 0) { - /* Something really bad happened */ - perror("Can't read audio data"); - break; - } - if (size < codesize) { - /* Not enough data for encoding even a single frame */ - break; - } - /* encode all the data from the input buffer in a loop */ - inp = input; - outp = output; - while (size >= codesize) { - len = sbc_encode(&sbc, inp, codesize, - outp, sizeof(output) - (outp - output), - &encoded); - if (len != codesize || encoded <= 0) { - fprintf(stderr, - "sbc_encode fail, len=%zd, encoded=%lu\n", - len, (unsigned long) encoded); - break; - } - size -= len; - inp += len; - outp += encoded; - } - len = write(fileno(stdout), output, outp - output); - if (len != outp - output) { - perror("Can't write SBC output"); - break; - } - if (size != 0) { - /* - * sbc_encode failure has been detected earlier or end - * of file reached (have trailing partial data which is - * insufficient to encode SBC frame) - */ - 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-2010 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-b, --bitpool Bitpool value (default is 32)\n" - "\t-j, --joint Joint stereo\n" - "\t-d, --dualchannel Dual channel\n" - "\t-S, --snr Use SNR mode (default is loudness)\n" - "\t-B, --blocks Number of blocks (4, 8, 12 or 16)\n" - "\n"); -} - -static struct option main_options[] = { - { "help", 0, 0, 'h' }, - { "verbose", 0, 0, 'v' }, - { "subbands", 1, 0, 's' }, - { "bitpool", 1, 0, 'b' }, - { "joint", 0, 0, 'j' }, - { "dualchannel",0, 0, 'd' }, - { "snr", 0, 0, 'S' }, - { "blocks", 1, 0, 'B' }, - { 0, 0, 0, 0 } -}; - -int main(int argc, char *argv[]) -{ - int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0; - int snr = 0, blocks = 16; - - while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:", - main_options, NULL)) != -1) { - switch(opt) { - case 'h': - usage(); - exit(0); - - case 'v': - verbose = 1; - break; - - case 's': - subbands = atoi(optarg); - if (subbands != 8 && subbands != 4) { - fprintf(stderr, "Invalid subbands\n"); - exit(1); - } - break; - - case 'b': - bitpool = atoi(optarg); - break; - - case 'j': - joint = 1; - break; - - case 'd': - dualchannel = 1; - break; - - case 'S': - snr = 1; - break; - - case 'B': - blocks = atoi(optarg); - if (blocks != 16 && blocks != 12 && - blocks != 8 && blocks != 4) { - fprintf(stderr, "Invalid blocks\n"); - exit(1); - } - break; - - default: - usage(); - exit(1); - } - } - - argc -= optind; - argv += optind; - optind = 0; - - if (argc < 1) { - usage(); - exit(1); - } - - for (i = 0; i < argc; i++) - encode(argv[i], subbands, bitpool, joint, dualchannel, - snr, blocks); - - return 0; -} diff --git a/sbc/sbcinfo.c b/sbc/sbcinfo.c deleted file mode 100644 index 8cfb54a..0000000 --- a/sbc/sbcinfo.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * - * Bluetooth low-complexity, subband codec (SBC) library - * - * Copyright (C) 2008-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#if __BYTE_ORDER == __LITTLE_ENDIAN -struct sbc_frame_hdr { - uint8_t syncword:8; /* Sync word */ - uint8_t subbands:1; /* Subbands */ - uint8_t allocation_method:1; /* Allocation method */ - uint8_t channel_mode:2; /* Channel mode */ - uint8_t blocks:2; /* Blocks */ - uint8_t sampling_frequency:2; /* Sampling frequency */ - uint8_t bitpool:8; /* Bitpool */ - uint8_t crc_check:8; /* CRC check */ -} __attribute__ ((packed)); -#elif __BYTE_ORDER == __BIG_ENDIAN -struct sbc_frame_hdr { - uint8_t syncword:8; /* Sync word */ - uint8_t sampling_frequency:2; /* Sampling frequency */ - uint8_t blocks:2; /* Blocks */ - uint8_t channel_mode:2; /* Channel mode */ - uint8_t allocation_method:1; /* Allocation method */ - uint8_t subbands:1; /* Subbands */ - uint8_t bitpool:8; /* Bitpool */ - uint8_t crc_check:8; /* CRC check */ -} __attribute__ ((packed)); -#else -#error "Unknown byte order" -#endif - -static int calc_frame_len(struct sbc_frame_hdr *hdr) -{ - int tmp, nrof_subbands, nrof_blocks; - - nrof_subbands = (hdr->subbands + 1) * 4; - nrof_blocks = (hdr->blocks + 1) * 4; - - switch (hdr->channel_mode) { - case 0x00: - nrof_subbands /= 2; - tmp = nrof_blocks * hdr->bitpool; - break; - case 0x01: - tmp = nrof_blocks * hdr->bitpool * 2; - break; - case 0x02: - tmp = nrof_blocks * hdr->bitpool; - break; - case 0x03: - tmp = nrof_blocks * hdr->bitpool + nrof_subbands; - break; - default: - return 0; - } - - return (nrof_subbands + ((tmp + 7) / 8)); -} - -static double calc_bit_rate(struct sbc_frame_hdr *hdr) -{ - int nrof_subbands, nrof_blocks; - double f; - - nrof_subbands = (hdr->subbands + 1) * 4; - nrof_blocks = (hdr->blocks + 1) * 4; - - switch (hdr->sampling_frequency) { - case 0: - f = 16; - break; - case 1: - f = 32; - break; - case 2: - f = 44.1; - break; - case 3: - f = 48; - break; - default: - return 0; - } - - return ((8 * (calc_frame_len(hdr) + 4) * f) / - (nrof_subbands * nrof_blocks)); -} - -static char *freq2str(uint8_t freq) -{ - switch (freq) { - case 0: - return "16 kHz"; - case 1: - return "32 kHz"; - case 2: - return "44.1 kHz"; - case 3: - return "48 kHz"; - default: - return "Unknown"; - } -} - -static char *mode2str(uint8_t mode) -{ - switch (mode) { - case 0: - return "Mono"; - case 1: - return "Dual Channel"; - case 2: - return "Stereo"; - case 3: - return "Joint Stereo"; - default: - return "Unknown"; - } -} - -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; -} - -#define SIZE 32 - -static int analyze_file(char *filename) -{ - struct sbc_frame_hdr hdr; - unsigned char buf[64]; - double rate; - int bitpool[SIZE], frame_len[SIZE]; - int subbands, blocks, freq, method; - int n, p1, p2, fd, size, num; - ssize_t len; - unsigned int count; - - if (strcmp(filename, "-")) { - printf("Filename\t\t%s\n", basename(filename)); - - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror("Can't open file"); - return -1; - } - } else - fd = fileno(stdin); - - len = __read(fd, &hdr, sizeof(hdr)); - if (len != sizeof(hdr) || hdr.syncword != 0x9c) { - fprintf(stderr, "Not a SBC audio file\n"); - return -1; - } - - subbands = (hdr.subbands + 1) * 4; - blocks = (hdr.blocks + 1) * 4; - freq = hdr.sampling_frequency; - method = hdr.allocation_method; - - count = calc_frame_len(&hdr); - - bitpool[0] = hdr.bitpool; - frame_len[0] = count + 4; - - for (n = 1; n < SIZE; n++) { - bitpool[n] = 0; - frame_len[n] = 0; - } - - if (lseek(fd, 0, SEEK_SET) < 0) { - num = 1; - rate = calc_bit_rate(&hdr); - while (count) { - size = count > sizeof(buf) ? sizeof(buf) : count; - len = __read(fd, buf, size); - if (len < 0) - break; - count -= len; - } - } else { - num = 0; - rate = 0; - } - - while (1) { - len = __read(fd, &hdr, sizeof(hdr)); - if (len < 0) { - fprintf(stderr, "Unable to read frame header" - " (error %d)\n", errno); - break; - } - - if (len == 0) - break; - - if ((size_t) len < sizeof(hdr) || hdr.syncword != 0x9c) { - fprintf(stderr, "Corrupted SBC stream " - "(len %zd syncword 0x%02x)\n", - len, hdr.syncword); - break; - } - - count = calc_frame_len(&hdr); - len = count + 4; - - p1 = -1; - p2 = -1; - for (n = 0; n < SIZE; n++) { - if (p1 < 0 && (bitpool[n] == 0 || bitpool[n] == hdr.bitpool)) - p1 = n; - if (p2 < 0 && (frame_len[n] == 0 || frame_len[n] == len)) - p2 = n; - } - if (p1 >= 0) - bitpool[p1] = hdr.bitpool; - if (p2 >= 0) - frame_len[p2] = len; - - while (count) { - size = count > sizeof(buf) ? sizeof(buf) : count; - - len = __read(fd, buf, size); - if (len != size) { - fprintf(stderr, "Unable to read frame data " - "(error %d)\n", errno); - break; - } - - count -= len; - } - - rate += calc_bit_rate(&hdr); - num++; - } - - printf("Subbands\t\t%d\n", subbands); - printf("Block length\t\t%d\n", blocks); - printf("Sampling frequency\t%s\n", freq2str(freq)); - printf("Channel mode\t\t%s\n", mode2str(hdr.channel_mode)); - printf("Allocation method\t%s\n", method ? "SNR" : "Loudness"); - printf("Bitpool\t\t\t%d", bitpool[0]); - for (n = 1; n < SIZE; n++) - if (bitpool[n] > 0) - printf(", %d", bitpool[n]); - printf("\n"); - printf("Number of frames\t%d\n", num); - printf("Frame length\t\t%d", frame_len[0]); - for (n = 1; n < SIZE; n++) - if (frame_len[n] > 0) - printf(", %d", frame_len[n]); - printf(" Bytes\n"); - if (num > 0) - printf("Bit rate\t\t%.3f kbps\n", rate / num); - - if (fd > fileno(stderr)) - close(fd); - - printf("\n"); - - return 0; -} - -int main(int argc, char *argv[]) -{ - int i; - - if (argc < 2) { - fprintf(stderr, "Usage: sbcinfo \n"); - exit(1); - } - - for (i = 0; i < argc - 1; i++) - if (analyze_file(argv[i + 1]) < 0) - exit(1); - - return 0; -} diff --git a/src/formats.h b/src/formats.h new file mode 100644 index 0000000..3050b25 --- /dev/null +++ b/src/formats.h @@ -0,0 +1,55 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#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) */ +}; diff --git a/src/sbcdec.c b/src/sbcdec.c new file mode 100644 index 0000000..0077a82 --- /dev/null +++ b/src/sbcdec.c @@ -0,0 +1,293 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) decoder + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sbc/sbc.h" +#include "formats.h" + +#define BUF_SIZE 8192 + +static int verbose = 0; + +static void decode(char *filename, char *output, int tofile) +{ + unsigned char buf[BUF_SIZE], *stream; + struct stat st; + sbc_t sbc; + int fd, ad, pos, streamlen, framelen, count; + size_t len; + int format = AFMT_S16_BE, frequency, channels; + ssize_t written; + + if (stat(filename, &st) < 0) { + fprintf(stderr, "Can't get size of file %s: %s\n", + filename, strerror(errno)); + return; + } + + 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; + + if (tofile) + ad = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644); + else + ad = open(output, O_WRONLY, 0); + + if (ad < 0) { + fprintf(stderr, "Can't open output %s: %s\n", + output, strerror(errno)); + goto free; + } + + sbc_init(&sbc, 0L); + sbc.endian = SBC_BE; + + framelen = sbc_decode(&sbc, stream, streamlen, buf, sizeof(buf), &len); + channels = sbc.mode == SBC_MODE_MONO ? 1 : 2; + switch (sbc.frequency) { + case SBC_FREQ_16000: + frequency = 16000; + break; + + case SBC_FREQ_32000: + frequency = 32000; + break; + + case SBC_FREQ_44100: + frequency = 44100; + break; + + case SBC_FREQ_48000: + frequency = 48000; + break; + default: + frequency = 0; + } + + if (verbose) { + fprintf(stderr,"decoding %s with rate %d, %d subbands, " + "%d bits, allocation method %s and mode %s\n", + filename, frequency, sbc.subbands * 4 + 4, sbc.bitpool, + sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS", + sbc.mode == SBC_MODE_MONO ? "MONO" : + sbc.mode == SBC_MODE_STEREO ? + "STEREO" : "JOINTSTEREO"); + } + + if (tofile) { + struct au_header au_hdr; + + au_hdr.magic = AU_MAGIC; + au_hdr.hdr_size = BE_INT(24); + au_hdr.data_size = BE_INT(0); + au_hdr.encoding = BE_INT(AU_FMT_LIN16); + au_hdr.sample_rate = BE_INT(frequency); + au_hdr.channels = BE_INT(channels); + + written = write(ad, &au_hdr, sizeof(au_hdr)); + if (written < (ssize_t) sizeof(au_hdr)) { + fprintf(stderr, "Failed to write header\n"); + goto close; + } + } else { + if (ioctl(ad, SNDCTL_DSP_SETFMT, &format) < 0) { + fprintf(stderr, "Can't set audio format on %s: %s\n", + output, strerror(errno)); + goto close; + } + + if (ioctl(ad, SNDCTL_DSP_CHANNELS, &channels) < 0) { + fprintf(stderr, "Can't set number of channels on %s: %s\n", + output, strerror(errno)); + goto close; + } + + if (ioctl(ad, SNDCTL_DSP_SPEED, &frequency) < 0) { + fprintf(stderr, "Can't set audio rate on %s: %s\n", + output, strerror(errno)); + goto close; + } + } + + count = len; + + 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 + len >= BUF_SIZE) { + /* buffer is too full to stuff decoded audio in so it + * must be written to the device */ + written = write(ad, buf, count); + if (written > 0) + count -= written; + } + + /* sanity check */ + if (count + len >= BUF_SIZE) { + fprintf(stderr, + "buffer size of %d is too small for decoded" + " data (%lu)\n", BUF_SIZE, (unsigned long) (len + count)); + exit(1); + } + + /* 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, + buf + count, sizeof(buf) - count, &len); + + /* increase the count */ + count += len; + } + + if (count > 0) { + written = write(ad, buf, count); + if (written > 0) + count -= written; + } + +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-2010 Marcel Holtmann\n\n"); + + printf("Usage:\n" + "\tsbcdec [options] file(s)\n" + "\n"); + + printf("Options:\n" + "\t-h, --help Display help\n" + "\t-v, --verbose Verbose mode\n" + "\t-d, --device Sound device\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 *output = NULL; + int i, opt, tofile = 0; + + while ((opt = getopt_long(argc, argv, "+hvd:f:", + main_options, NULL)) != -1) { + switch(opt) { + case 'h': + usage(); + exit(0); + + case 'v': + verbose = 1; + break; + + case 'd': + free(output); + output = strdup(optarg); + tofile = 0; + break; + + case 'f' : + free(output); + output = 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], output ? output : "/dev/dsp", tofile); + + free(output); + + return 0; +} diff --git a/src/sbcenc.c b/src/sbcenc.c new file mode 100644 index 0000000..a723b03 --- /dev/null +++ b/src/sbcenc.c @@ -0,0 +1,308 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) encoder + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sbc/sbc.h" +#include "formats.h" + +static int verbose = 0; + +#define BUF_SIZE 32768 +static unsigned char input[BUF_SIZE], output[BUF_SIZE + BUF_SIZE / 4]; + +static void encode(char *filename, int subbands, int bitpool, int joint, + int dualchannel, int snr, int blocks) +{ + struct au_header au_hdr; + sbc_t sbc; + int fd, size, srate, codesize, nframes; + ssize_t encoded; + ssize_t len; + + if (sizeof(au_hdr) != 24) { + /* Sanity check just in case */ + fprintf(stderr, "FIXME: sizeof(au_hdr) != 24\n"); + return; + } + + 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, &au_hdr, sizeof(au_hdr)); + if (len < (ssize_t) 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; + } + + if (au_hdr.magic != AU_MAGIC || + BE_INT(au_hdr.hdr_size) > 128 || + BE_INT(au_hdr.hdr_size) < sizeof(au_hdr) || + BE_INT(au_hdr.encoding) != AU_FMT_LIN16) { + fprintf(stderr, "Not in Sun/NeXT audio S16_BE format\n"); + goto done; + } + + sbc_init(&sbc, 0L); + + switch (BE_INT(au_hdr.sample_rate)) { + case 16000: + sbc.frequency = SBC_FREQ_16000; + break; + case 32000: + sbc.frequency = SBC_FREQ_32000; + break; + case 44100: + sbc.frequency = SBC_FREQ_44100; + break; + case 48000: + sbc.frequency = SBC_FREQ_48000; + break; + } + + srate = BE_INT(au_hdr.sample_rate); + + sbc.subbands = subbands == 4 ? SBC_SB_4 : SBC_SB_8; + + if (BE_INT(au_hdr.channels) == 1) { + sbc.mode = SBC_MODE_MONO; + if (joint || dualchannel) { + fprintf(stderr, "Audio is mono but joint or " + "dualchannel mode has been specified\n"); + goto done; + } + } else if (joint && !dualchannel) + sbc.mode = SBC_MODE_JOINT_STEREO; + else if (!joint && dualchannel) + sbc.mode = SBC_MODE_DUAL_CHANNEL; + else if (!joint && !dualchannel) + sbc.mode = SBC_MODE_STEREO; + else { + fprintf(stderr, "Both joint and dualchannel mode have been " + "specified\n"); + goto done; + } + + sbc.endian = SBC_BE; + /* Skip extra bytes of the header if any */ + if (read(fd, input, BE_INT(au_hdr.hdr_size) - len) < 0) + goto done; + + sbc.bitpool = bitpool; + sbc.allocation = snr ? SBC_AM_SNR : SBC_AM_LOUDNESS; + sbc.blocks = blocks == 4 ? SBC_BLK_4 : + blocks == 8 ? SBC_BLK_8 : + blocks == 12 ? SBC_BLK_12 : SBC_BLK_16; + + if (verbose) { + fprintf(stderr, "encoding %s with rate %d, %d blocks, " + "%d subbands, %d bits, allocation method %s, " + "and mode %s\n", + filename, srate, blocks, subbands, bitpool, + sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS", + sbc.mode == SBC_MODE_MONO ? "MONO" : + sbc.mode == SBC_MODE_STEREO ? + "STEREO" : "JOINTSTEREO"); + } + + codesize = sbc_get_codesize(&sbc); + nframes = sizeof(input) / codesize; + while (1) { + unsigned char *inp, *outp; + /* read data for up to 'nframes' frames of input data */ + size = read(fd, input, codesize * nframes); + if (size < 0) { + /* Something really bad happened */ + perror("Can't read audio data"); + break; + } + if (size < codesize) { + /* Not enough data for encoding even a single frame */ + break; + } + /* encode all the data from the input buffer in a loop */ + inp = input; + outp = output; + while (size >= codesize) { + len = sbc_encode(&sbc, inp, codesize, + outp, sizeof(output) - (outp - output), + &encoded); + if (len != codesize || encoded <= 0) { + fprintf(stderr, + "sbc_encode fail, len=%zd, encoded=%lu\n", + len, (unsigned long) encoded); + break; + } + size -= len; + inp += len; + outp += encoded; + } + len = write(fileno(stdout), output, outp - output); + if (len != outp - output) { + perror("Can't write SBC output"); + break; + } + if (size != 0) { + /* + * sbc_encode failure has been detected earlier or end + * of file reached (have trailing partial data which is + * insufficient to encode SBC frame) + */ + 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-2010 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-b, --bitpool Bitpool value (default is 32)\n" + "\t-j, --joint Joint stereo\n" + "\t-d, --dualchannel Dual channel\n" + "\t-S, --snr Use SNR mode (default is loudness)\n" + "\t-B, --blocks Number of blocks (4, 8, 12 or 16)\n" + "\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "verbose", 0, 0, 'v' }, + { "subbands", 1, 0, 's' }, + { "bitpool", 1, 0, 'b' }, + { "joint", 0, 0, 'j' }, + { "dualchannel",0, 0, 'd' }, + { "snr", 0, 0, 'S' }, + { "blocks", 1, 0, 'B' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0; + int snr = 0, blocks = 16; + + while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:", + main_options, NULL)) != -1) { + switch(opt) { + case 'h': + usage(); + exit(0); + + case 'v': + verbose = 1; + break; + + case 's': + subbands = atoi(optarg); + if (subbands != 8 && subbands != 4) { + fprintf(stderr, "Invalid subbands\n"); + exit(1); + } + break; + + case 'b': + bitpool = atoi(optarg); + break; + + case 'j': + joint = 1; + break; + + case 'd': + dualchannel = 1; + break; + + case 'S': + snr = 1; + break; + + case 'B': + blocks = atoi(optarg); + if (blocks != 16 && blocks != 12 && + blocks != 8 && blocks != 4) { + fprintf(stderr, "Invalid blocks\n"); + exit(1); + } + break; + + default: + usage(); + exit(1); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + for (i = 0; i < argc; i++) + encode(argv[i], subbands, bitpool, joint, dualchannel, + snr, blocks); + + return 0; +} diff --git a/src/sbcinfo.c b/src/sbcinfo.c new file mode 100644 index 0000000..8cfb54a --- /dev/null +++ b/src/sbcinfo.c @@ -0,0 +1,321 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +struct sbc_frame_hdr { + uint8_t syncword:8; /* Sync word */ + uint8_t subbands:1; /* Subbands */ + uint8_t allocation_method:1; /* Allocation method */ + uint8_t channel_mode:2; /* Channel mode */ + uint8_t blocks:2; /* Blocks */ + uint8_t sampling_frequency:2; /* Sampling frequency */ + uint8_t bitpool:8; /* Bitpool */ + uint8_t crc_check:8; /* CRC check */ +} __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN +struct sbc_frame_hdr { + uint8_t syncword:8; /* Sync word */ + uint8_t sampling_frequency:2; /* Sampling frequency */ + uint8_t blocks:2; /* Blocks */ + uint8_t channel_mode:2; /* Channel mode */ + uint8_t allocation_method:1; /* Allocation method */ + uint8_t subbands:1; /* Subbands */ + uint8_t bitpool:8; /* Bitpool */ + uint8_t crc_check:8; /* CRC check */ +} __attribute__ ((packed)); +#else +#error "Unknown byte order" +#endif + +static int calc_frame_len(struct sbc_frame_hdr *hdr) +{ + int tmp, nrof_subbands, nrof_blocks; + + nrof_subbands = (hdr->subbands + 1) * 4; + nrof_blocks = (hdr->blocks + 1) * 4; + + switch (hdr->channel_mode) { + case 0x00: + nrof_subbands /= 2; + tmp = nrof_blocks * hdr->bitpool; + break; + case 0x01: + tmp = nrof_blocks * hdr->bitpool * 2; + break; + case 0x02: + tmp = nrof_blocks * hdr->bitpool; + break; + case 0x03: + tmp = nrof_blocks * hdr->bitpool + nrof_subbands; + break; + default: + return 0; + } + + return (nrof_subbands + ((tmp + 7) / 8)); +} + +static double calc_bit_rate(struct sbc_frame_hdr *hdr) +{ + int nrof_subbands, nrof_blocks; + double f; + + nrof_subbands = (hdr->subbands + 1) * 4; + nrof_blocks = (hdr->blocks + 1) * 4; + + switch (hdr->sampling_frequency) { + case 0: + f = 16; + break; + case 1: + f = 32; + break; + case 2: + f = 44.1; + break; + case 3: + f = 48; + break; + default: + return 0; + } + + return ((8 * (calc_frame_len(hdr) + 4) * f) / + (nrof_subbands * nrof_blocks)); +} + +static char *freq2str(uint8_t freq) +{ + switch (freq) { + case 0: + return "16 kHz"; + case 1: + return "32 kHz"; + case 2: + return "44.1 kHz"; + case 3: + return "48 kHz"; + default: + return "Unknown"; + } +} + +static char *mode2str(uint8_t mode) +{ + switch (mode) { + case 0: + return "Mono"; + case 1: + return "Dual Channel"; + case 2: + return "Stereo"; + case 3: + return "Joint Stereo"; + default: + return "Unknown"; + } +} + +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; +} + +#define SIZE 32 + +static int analyze_file(char *filename) +{ + struct sbc_frame_hdr hdr; + unsigned char buf[64]; + double rate; + int bitpool[SIZE], frame_len[SIZE]; + int subbands, blocks, freq, method; + int n, p1, p2, fd, size, num; + ssize_t len; + unsigned int count; + + if (strcmp(filename, "-")) { + printf("Filename\t\t%s\n", basename(filename)); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("Can't open file"); + return -1; + } + } else + fd = fileno(stdin); + + len = __read(fd, &hdr, sizeof(hdr)); + if (len != sizeof(hdr) || hdr.syncword != 0x9c) { + fprintf(stderr, "Not a SBC audio file\n"); + return -1; + } + + subbands = (hdr.subbands + 1) * 4; + blocks = (hdr.blocks + 1) * 4; + freq = hdr.sampling_frequency; + method = hdr.allocation_method; + + count = calc_frame_len(&hdr); + + bitpool[0] = hdr.bitpool; + frame_len[0] = count + 4; + + for (n = 1; n < SIZE; n++) { + bitpool[n] = 0; + frame_len[n] = 0; + } + + if (lseek(fd, 0, SEEK_SET) < 0) { + num = 1; + rate = calc_bit_rate(&hdr); + while (count) { + size = count > sizeof(buf) ? sizeof(buf) : count; + len = __read(fd, buf, size); + if (len < 0) + break; + count -= len; + } + } else { + num = 0; + rate = 0; + } + + while (1) { + len = __read(fd, &hdr, sizeof(hdr)); + if (len < 0) { + fprintf(stderr, "Unable to read frame header" + " (error %d)\n", errno); + break; + } + + if (len == 0) + break; + + if ((size_t) len < sizeof(hdr) || hdr.syncword != 0x9c) { + fprintf(stderr, "Corrupted SBC stream " + "(len %zd syncword 0x%02x)\n", + len, hdr.syncword); + break; + } + + count = calc_frame_len(&hdr); + len = count + 4; + + p1 = -1; + p2 = -1; + for (n = 0; n < SIZE; n++) { + if (p1 < 0 && (bitpool[n] == 0 || bitpool[n] == hdr.bitpool)) + p1 = n; + if (p2 < 0 && (frame_len[n] == 0 || frame_len[n] == len)) + p2 = n; + } + if (p1 >= 0) + bitpool[p1] = hdr.bitpool; + if (p2 >= 0) + frame_len[p2] = len; + + while (count) { + size = count > sizeof(buf) ? sizeof(buf) : count; + + len = __read(fd, buf, size); + if (len != size) { + fprintf(stderr, "Unable to read frame data " + "(error %d)\n", errno); + break; + } + + count -= len; + } + + rate += calc_bit_rate(&hdr); + num++; + } + + printf("Subbands\t\t%d\n", subbands); + printf("Block length\t\t%d\n", blocks); + printf("Sampling frequency\t%s\n", freq2str(freq)); + printf("Channel mode\t\t%s\n", mode2str(hdr.channel_mode)); + printf("Allocation method\t%s\n", method ? "SNR" : "Loudness"); + printf("Bitpool\t\t\t%d", bitpool[0]); + for (n = 1; n < SIZE; n++) + if (bitpool[n] > 0) + printf(", %d", bitpool[n]); + printf("\n"); + printf("Number of frames\t%d\n", num); + printf("Frame length\t\t%d", frame_len[0]); + for (n = 1; n < SIZE; n++) + if (frame_len[n] > 0) + printf(", %d", frame_len[n]); + printf(" Bytes\n"); + if (num > 0) + printf("Bit rate\t\t%.3f kbps\n", rate / num); + + if (fd > fileno(stderr)) + close(fd); + + printf("\n"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int i; + + if (argc < 2) { + fprintf(stderr, "Usage: sbcinfo \n"); + exit(1); + } + + for (i = 0; i < argc - 1; i++) + if (analyze_file(argv[i + 1]) < 0) + exit(1); + + return 0; +} -- cgit v1.2.1