diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2012-07-29 20:00:29 -0700 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2012-07-29 20:00:29 -0700 |
commit | 91815a03cf63a859a5bf5a46c3d5d40337853767 (patch) | |
tree | c9abe0a013741c5f59b34ce928912e674a8df08b /src | |
parent | fab7c1faab81fac92346b1da0a062d6626b37deb (diff) | |
download | sbc-91815a03cf63a859a5bf5a46c3d5d40337853767.tar.gz |
src: Move subband encoder, decoder and info tools
Diffstat (limited to 'src')
-rw-r--r-- | src/formats.h | 55 | ||||
-rw-r--r-- | src/sbcdec.c | 293 | ||||
-rw-r--r-- | src/sbcenc.c | 308 | ||||
-rw-r--r-- | src/sbcinfo.c | 321 |
4 files changed, 977 insertions, 0 deletions
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 <marcel@holtmann.org> + * + * + * 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 <byteswap.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) */ +}; 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 <marcel@holtmann.org> + * + * + * 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 <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> + +#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 <dsp> Sound device\n" + "\t-f, --file <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 <marcel@holtmann.org> + * + * + * 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 <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <sys/stat.h> + +#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 <marcel@holtmann.org> + * + * + * 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 <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <libgen.h> + +#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 <file>\n"); + exit(1); + } + + for (i = 0; i < argc - 1; i++) + if (analyze_file(argv[i + 1]) < 0) + exit(1); + + return 0; +} |