2012-09-27 14:44:23

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 0/5] mSBC tests

Hi folks,

I wanted to know more about mSBC and I look into the spec and saw it was just
a set of settings for SBC. So I tried to exercise them using sbcenc and sbcdec
and found it wasn't possible, I ended modifying SBC library and now it should
be possible to use mSBC settings.
Using 15 block doesn't work using SIMD processing because of data
reordering so I recalled some legacy C code from an older release
(sbc_analyse_eight) and put in a separate primitives file.

Work done from the spec, not tested with any reference encoder.

How to use:
sample.au should be an .au audio file 16000hz 16bits 1 channel pcm.
$ src/sbcenc -m -b26 -B16 -s8 sample.au > sample.au.msbc
$ src/sbcinfo sample.au.msbc
$ src/sbcdec -m -f sample.au.msbc.au sample.au.msbc
$ mplayer sample.au.msbc.au

Note sure there is any application yet but I thought I should share.

Regards,
Frederic


Frédéric Dalleau (5):
Add msbc encoding and decoding flag
Add support for mSBC frame header
update sbcdec for msbc
update sbcenc for msbc
update sbcinfo for msbc

Makefile.am | 1 +
sbc/sbc.c | 227 +++++++++++++++++++-------------
sbc/sbc.h | 3 +
sbc/sbc_primitives.c | 8 +-
sbc/sbc_primitives.h | 7 +-
sbc/sbc_primitives_stdc.c | 321 +++++++++++++++++++++++++++++++++++++++++++++
sbc/sbc_primitives_stdc.h | 36 +++++
src/sbcdec.c | 17 ++-
src/sbcenc.c | 25 +++-
src/sbcinfo.c | 51 ++++---
10 files changed, 574 insertions(+), 122 deletions(-)
create mode 100644 sbc/sbc_primitives_stdc.c
create mode 100644 sbc/sbc_primitives_stdc.h

--
1.7.9.5



2012-09-28 15:05:42

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 0/5] mSBC tests

Hi Siarhei,

> > I wanted to know more about mSBC and I look into the spec and saw it
> > was just a set of settings for SBC. So I tried to exercise them using
> > sbcenc and sbcdec and found it wasn't possible, I ended modifying SBC
> > library and now it should be possible to use mSBC settings.
> > Using 15 block doesn't work using SIMD processing because of data
> > reordering so I recalled some legacy C code from an older release
> > (sbc_analyse_eight) and put in a separate primitives file.
>
>
> I wonder why they decided to go with the odd sided blocks? Anyway,
> I guess there is no other way but to adapt to this extended spec.
> Thanks for taking a look at this stuff.
>
> Have you tried to tweak the SIMD code to make it process one
> block at a time instead of batches of 4?
>
> I suspect that we might only need to track the state and flip between
> "even" and "odd" constant tables for every other processed block in
> "sbc_analyze_4b_4s_simd" and "sbc_analyze_4b_8s_simd" replacement
> functions. The functions "sbc_encoder_process_input_s4_internal"
> and "sbc_encoder_process_input_s8_internal" also need to be
> updated to handle arbitrary number of blocks and not just
> multiples of 4. Though I haven't been working with sbc code lately
> and may be mistaken.
>
> Using the old tables "_sbc_proto_fixed8" and "_sbc_proto_fixed4" has
> a disadvantage of not having the tweaks from:
> http://git.kernel.org/?p=bluetooth/sbc.git;a=commit;h=bec3d3989ba2
> They haven't been in use for ages and just remained in the sources by.
>
> If we just could make SIMD code work, that would be the best option in
> my opinion.
>
> > Work done from the spec, not tested with any reference encoder.
>
> Does a reference mSBC encoder exist somewhere? Or any ideas how to test
> the new code with something other than bluez just to be sure that it's
> correct?

I looked at the Bluetooth SIG website and couldn't find a reference
encoder or sample files. The old SBC reference encoder and decoder don't
seem to support other block sizes.

SBC Encoder LIB Version 1.5
Copyright (c) 2002 Philips Consumer Electronics, ASA Labs

Usage:
sbc_encoder [-jsv] [-lblk_len] [-nsubbands] [-p] [-rrate] [-ooutputfile] inputfile
[-s] use the stereo mode for stereo signals
[-v] verbose mode
[-j] allow the use of joint coding for stereo signals
[-lblk_len] blk_len specifies the APCM block length, out of [4,8,12,16]
[-nsubbands] subbands specifies the number of subbands, out of [4,8]
[-p] a simple psycho acoustic model is used
[-rrate] specifies the bit rate in bps
[-ooutputfile] specifies the name of the bitstream output file
inputfile specifies the audio input file, the major audio formats are supported


SBC Decoder LIB Version 1.5
Copyright (c) 2002 Philips Consumer Electronics, ASA Labs

Usage:
sbc_decoder [-v] [-ooutputfile] [-pstartpos] inputfile
[-v] verbose mode
[-pstartpos] startpos specifies the byte offset to start with decoding
[-ooutputfile] specifies the name of the audio output file
inputfile specifies the name of the bitstream input file

And the sample files are all from 2001. We might have to create our own
ones and our own naive and stupid reference encoder/decoder.

Regards

Marcel



2012-09-28 14:53:02

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 1/5] Add msbc encoding and decoding flag

Hi Fred,

> Also enable 15 blocks support using stdc sbc primitives
> ---
> Makefile.am | 1 +
> sbc/sbc.c | 123 +++++++++--------
> sbc/sbc.h | 3 +
> sbc/sbc_primitives.c | 8 +-
> sbc/sbc_primitives.h | 7 +-
> sbc/sbc_primitives_stdc.c | 321 +++++++++++++++++++++++++++++++++++++++++++++
> sbc/sbc_primitives_stdc.h | 36 +++++
> 7 files changed, 443 insertions(+), 56 deletions(-)
> create mode 100644 sbc/sbc_primitives_stdc.c
> create mode 100644 sbc/sbc_primitives_stdc.h

can you split this patch into at least two?

> diff --git a/Makefile.am b/Makefile.am
> index cad6a3b..75e3a4a 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -14,6 +14,7 @@ sbc_headers = sbc/sbc.h
>
> sbc_sources = sbc/sbc.c sbc/sbc_private.h sbc/sbc_math.h sbc/sbc_tables.h \
> sbc/sbc_primitives.h sbc/sbc_primitives.c \
> + sbc/sbc_primitives_stdc.h sbc/sbc_primitives_stdc.c \
> sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
> sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
> sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
> diff --git a/sbc/sbc.c b/sbc/sbc.c
> index f0c77c7..7e4faa0 100644
> --- a/sbc/sbc.c
> +++ b/sbc/sbc.c
> @@ -6,6 +6,7 @@
> * Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
> * Copyright (C) 2004-2005 Henryk Ploetz <[email protected]>
> * Copyright (C) 2005-2008 Brad Midgley <[email protected]>
> + * Copyright (C) 2012 Intel Corporation
> *
> *
> * This library is free software; you can redistribute it and/or
> @@ -51,6 +52,17 @@
> #include "sbc_primitives.h"
>
> #define SBC_SYNCWORD 0x9C
> +#define MSBC_SYNCWORD 0xAD
> +#define SBC_BLOCKS(sbc, blocks) (((sbc)->flags & SBC_MSBC) \
> + ? MSBC_BLOCKS : (blocks))

Not sure about this macro. Why do we need this? Wouldn't it be better to
make it explicit in the code.

> +#define MSBC_BLOCKMODE SBC_BLK_16
> +#define MSBC_BLOCKS 15
> +#define MSBC_BITPOOL 26
> +#define MSBC_SUBBAND_MODE 1
> +#define MSBC_SUBBANDS 8
> +#define MSBC_CHANNEL 1
> +#define MSBC_ALLOCATION SBC_AM_LOUDNESS

And as a nitpick, keep an empty line between SBC_ and MSBC_ constants.

> /* This structure contains an unpacked SBC frame.
> Yes, there is probably quite some unused space herein */
> @@ -373,9 +385,11 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
> * -2 Sync byte incorrect
> * -3 CRC8 incorrect
> * -4 Bitpool value out of bounds
> + * -5 msbc reserved byte 1 not 0
> + * -6 msbc reserved byte 2 not 0
> */

What are you doing with these return values. If nothing, then just
return sync byte incorrect.

> -static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
> - size_t len)
> +static int sbc_unpack_frame(sbc_t *sbc, const uint8_t *data,
> + struct sbc_frame *frame, size_t len)
> {

As in the comment from the other patch, instead of changing the function
prototype, just provide two separate function, that we choose from on
sbc_init.

> unsigned int consumed;
> /* Will copy the parts of the header that are relevant to crc
> @@ -413,6 +427,8 @@ static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
> frame->blocks = 16;
> break;
> }
> + if (sbc->flags & SBC_MSBC)
> + frame->blocks = MSBC_BLOCKS;
>
> frame->mode = (data[1] >> 2) & 0x03;
> switch (frame->mode) {
> @@ -690,13 +706,13 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
> for (ch = 0; ch < frame->channels; ch++) {
> x = &state->X[ch][state->position - 16 +
> frame->blocks * 4];
> - for (blk = 0; blk < frame->blocks; blk += 4) {
> + for (blk = 0; blk < frame->blocks; blk += state->inc) {
> state->sbc_analyze_4b_4s(
> x,
> frame->sb_sample_f[blk][ch],
> frame->sb_sample_f[blk + 1][ch] -
> frame->sb_sample_f[blk][ch]);
> - x -= 16;
> + x -= 4 * state->inc;
> }
> }
> return frame->blocks * 4;
> @@ -705,13 +721,13 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
> for (ch = 0; ch < frame->channels; ch++) {
> x = &state->X[ch][state->position - 32 +
> frame->blocks * 8];
> - for (blk = 0; blk < frame->blocks; blk += 4) {
> + for (blk = 0; blk < frame->blocks; blk += state->inc) {
> state->sbc_analyze_4b_8s(
> x,
> frame->sb_sample_f[blk][ch],
> frame->sb_sample_f[blk + 1][ch] -
> frame->sb_sample_f[blk][ch]);
> - x -= 32;
> + x -= 8 * state->inc;
> }
> }
> return frame->blocks * 8;
> @@ -764,10 +780,9 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
> * -99 not implemented
> */
>
> -static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
> - struct sbc_frame *frame, size_t len,
> - int frame_subbands, int frame_channels,
> - int joint)
> +static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(sbc_t *sbc,
> + uint8_t *data, struct sbc_frame *frame, size_t len,
> + int frame_subbands, int frame_channels, int joint)
> {
> /* Bitstream writer starts from the fourth byte */
> uint8_t *data_ptr = data + 4;
> @@ -785,37 +800,37 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
> uint32_t levels[2][8]; /* levels are derived from that */
> uint32_t sb_sample_delta[2][8];
>
> - data[0] = SBC_SYNCWORD;
> + data[0] = SBC_SYNCWORD;
>
> - data[1] = (frame->frequency & 0x03) << 6;
> + data[1] = (frame->frequency & 0x03) << 6;
>
> - data[1] |= (frame->block_mode & 0x03) << 4;
> + data[1] |= (frame->block_mode & 0x03) << 4;
>
> - data[1] |= (frame->mode & 0x03) << 2;
> + data[1] |= (frame->mode & 0x03) << 2;
>
> - data[1] |= (frame->allocation & 0x01) << 1;
> + data[1] |= (frame->allocation & 0x01) << 1;
>
> - switch (frame_subbands) {
> - case 4:
> - /* Nothing to do */
> - break;
> - case 8:
> - data[1] |= 0x01;
> - break;
> - default:
> - return -4;
> - break;
> - }
> + switch (frame_subbands) {
> + case 4:
> + /* Nothing to do */
> + break;
> + case 8:
> + data[1] |= 0x01;
> + break;
> + default:
> + return -4;
> + break;
> + }
>
> - data[2] = frame->bitpool;
> + data[2] = frame->bitpool;
>
> - if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
> - frame->bitpool > frame_subbands << 4)
> - return -5;
> + if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
> + frame->bitpool > frame_subbands << 4)
> + return -5;
>
> - if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
> - frame->bitpool > frame_subbands << 5)
> - return -5;
> + if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
> + frame->bitpool > frame_subbands << 5)
> + return -5;
>
> /* Can't fill in crc yet */
>
> @@ -881,33 +896,33 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
> return data_ptr - data;
> }
>
> -static ssize_t sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len,
> - int joint)
> +static ssize_t sbc_pack_frame(sbc_t *sbc, uint8_t *data,
> + struct sbc_frame *frame, size_t len, int joint)
> {
> if (frame->subbands == 4) {
> if (frame->channels == 1)
> return sbc_pack_frame_internal(
> - data, frame, len, 4, 1, joint);
> + sbc, data, frame, len, 4, 1, joint);
> else
> return sbc_pack_frame_internal(
> - data, frame, len, 4, 2, joint);
> + sbc, data, frame, len, 4, 2, joint);
> } else {
> if (frame->channels == 1)
> return sbc_pack_frame_internal(
> - data, frame, len, 8, 1, joint);
> + sbc, data, frame, len, 8, 1, joint);
> else
> return sbc_pack_frame_internal(
> - data, frame, len, 8, 2, joint);
> + sbc, data, frame, len, 8, 2, joint);
> }
> }
>
> -static void sbc_encoder_init(struct sbc_encoder_state *state,
> +static void sbc_encoder_init(int msbc, struct sbc_encoder_state *state,
> const struct sbc_frame *frame)
> {
> memset(&state->X, 0, sizeof(state->X));
> state->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7;
>
> - sbc_init_primitives(state);
> + sbc_init_primitives(msbc, state);
> }

I personally prefer having mSBC capable primitives. If this would
actually lead to some sort of issue, then we might need a different
option that allows us to fallback to non-optimized primitives. And then
we might not just bind this for mSBC, then we might just make that an
option for the library users.

Regards

Marcel



2012-09-28 14:45:11

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 2/5] Add support for mSBC frame header

Hi Fred,

> mSBC modifies header so that it contains: OxAD 0x00 0x00.
> The first bytes allows to distinguish mSBC packets from standard SBC
> packets used in A2DP. The two zero bytes are reserved for future definition.
> ---
> sbc/sbc.c | 104 +++++++++++++++++++++++++++++++++++++------------------------
> 1 file changed, 63 insertions(+), 41 deletions(-)
>
> diff --git a/sbc/sbc.c b/sbc/sbc.c
> index 7e4faa0..131755b 100644
> --- a/sbc/sbc.c
> +++ b/sbc/sbc.c
> @@ -407,55 +407,71 @@ static int sbc_unpack_frame(sbc_t *sbc, const uint8_t *data,
> if (len < 4)
> return -1;
>
> - if (data[0] != SBC_SYNCWORD)
> - return -2;
> -
> - frame->frequency = (data[1] >> 6) & 0x03;
> + if (sbc->flags & SBC_MSBC) {
> + if (data[0] != MSBC_SYNCWORD)
> + return -2;
> + if (data[1] != 0)
> + return -5;
> + if (data[2] != 0)
> + return -6;

I am getting the felling that just splitting this into separate
unpack_sbc_frame and unpack_msbc_frame would make this a lot cleaner
instead of keeping to check if mSBC flag or not is set.

We might even include a function callback in sbc_t where we on sbc_init
just select one or the other. No need to keep repeating that check for
every single frame. As far as I can tell, you never can switch from SBC
to mSBC or vice-versa anyway.

With that you also would not need to change the function prototype to
include sbc_t at all.

Regards

Marcel



2012-09-28 08:21:18

by Dalleau, Frederic

[permalink] [raw]
Subject: Re: [PATCH 0/5] mSBC tests

Hi Siarhei,

Thanks for reply,

On Thu, Sep 27, 2012 at 10:34 PM, Siarhei Siamashka
<[email protected]> wrote:
> Have you tried to tweak the SIMD code to make it process one
> block at a time instead of batches of 4?

I looked at sbc_analyze_4b_8s_simd but I missed the logic behind
reordering. That's
why I reused generic code in order to make sure that it would work.
If there is some interest I can look into it deeper. And if you have
any pointers
they are welcome.

> If we just could make SIMD code work, that would be the best option in
> my opinion.

Agreed, for sure.

>> Work done from the spec, not tested with any reference encoder.
>
> Does a reference mSBC encoder exist somewhere? Or any ideas how to test
> the new code with something other than bluez just to be sure that it's
> correct?

IIRC there used to be one, it was a windows program sbcencoder.exe but
I couldn't find
it anymore, and I'm not sure that it would support mSBC.

Regards,
Fr?d?ric

2012-09-27 20:34:20

by Siarhei Siamashka

[permalink] [raw]
Subject: Re: [PATCH 0/5] mSBC tests

On Thu, 27 Sep 2012 16:44:23 +0200
Frédéric Dalleau <[email protected]> wrote:

> Hi folks,
>
> I wanted to know more about mSBC and I look into the spec and saw it
> was just a set of settings for SBC. So I tried to exercise them using
> sbcenc and sbcdec and found it wasn't possible, I ended modifying SBC
> library and now it should be possible to use mSBC settings.
> Using 15 block doesn't work using SIMD processing because of data
> reordering so I recalled some legacy C code from an older release
> (sbc_analyse_eight) and put in a separate primitives file.

Hi,

I wonder why they decided to go with the odd sided blocks? Anyway,
I guess there is no other way but to adapt to this extended spec.
Thanks for taking a look at this stuff.

Have you tried to tweak the SIMD code to make it process one
block at a time instead of batches of 4?

I suspect that we might only need to track the state and flip between
"even" and "odd" constant tables for every other processed block in
"sbc_analyze_4b_4s_simd" and "sbc_analyze_4b_8s_simd" replacement
functions. The functions "sbc_encoder_process_input_s4_internal"
and "sbc_encoder_process_input_s8_internal" also need to be
updated to handle arbitrary number of blocks and not just
multiples of 4. Though I haven't been working with sbc code lately
and may be mistaken.

Using the old tables "_sbc_proto_fixed8" and "_sbc_proto_fixed4" has
a disadvantage of not having the tweaks from:
http://git.kernel.org/?p=bluetooth/sbc.git;a=commit;h=bec3d3989ba2
They haven't been in use for ages and just remained in the sources by.

If we just could make SIMD code work, that would be the best option in
my opinion.

> Work done from the spec, not tested with any reference encoder.

Does a reference mSBC encoder exist somewhere? Or any ideas how to test
the new code with something other than bluez just to be sure that it's
correct?

> How to use:
> sample.au should be an .au audio file 16000hz 16bits 1 channel pcm.
> $ src/sbcenc -m -b26 -B16 -s8 sample.au > sample.au.msbc
> $ src/sbcinfo sample.au.msbc
> $ src/sbcdec -m -f sample.au.msbc.au sample.au.msbc
> $ mplayer sample.au.msbc.au
>
> Note sure there is any application yet but I thought I should share.

Thanks for sharing this information and patches. I would love to try
experimenting with mSBC code myself (mostly curious to check whether
SIMD has any chance to remain useful), but don't have much time right
now and will be away from my primary computer for the whole next week.

--
Best regards,
Siarhei Siamashka

2012-09-27 14:44:27

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 4/5] update sbcenc for msbc

---
src/sbcenc.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/src/sbcenc.c b/src/sbcenc.c
index a723b03..71ad6bb 100644
--- a/src/sbcenc.c
+++ b/src/sbcenc.c
@@ -45,7 +45,7 @@ static int verbose = 0;
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)
+ int dualchannel, int snr, int blocks, int msbc)
{
struct au_header au_hdr;
sbc_t sbc;
@@ -87,7 +87,7 @@ static void encode(char *filename, int subbands, int bitpool, int joint,
goto done;
}

- sbc_init(&sbc, 0L);
+ sbc_init(&sbc, msbc ? SBC_MSBC : 0L);

switch (BE_INT(au_hdr.sample_rate)) {
case 16000:
@@ -215,6 +215,7 @@ static void usage(void)
printf("Options:\n"
"\t-h, --help Display help\n"
"\t-v, --verbose Verbose mode\n"
+ "\t-m, --msbc mSBC codec\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"
@@ -227,6 +228,7 @@ static void usage(void)
static struct option main_options[] = {
{ "help", 0, 0, 'h' },
{ "verbose", 0, 0, 'v' },
+ { "msbc", 0, 0, 'm' },
{ "subbands", 1, 0, 's' },
{ "bitpool", 1, 0, 'b' },
{ "joint", 0, 0, 'j' },
@@ -239,9 +241,9 @@ static struct option main_options[] = {
int main(int argc, char *argv[])
{
int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0;
- int snr = 0, blocks = 16;
+ int snr = 0, blocks = 16, msbc = 0;

- while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:",
+ while ((opt = getopt_long(argc, argv, "+hmvs:b:jdSB:",
main_options, NULL)) != -1) {
switch(opt) {
case 'h':
@@ -252,6 +254,10 @@ int main(int argc, char *argv[])
verbose = 1;
break;

+ case 'm':
+ msbc = 1;
+ break;
+
case 's':
subbands = atoi(optarg);
if (subbands != 8 && subbands != 4) {
@@ -300,9 +306,18 @@ int main(int argc, char *argv[])
exit(1);
}

+ if (msbc) {
+ subbands = 8;
+ bitpool = 26;
+ joint = 0;
+ dualchannel = 0;
+ snr = 0;
+ blocks = 15;
+ }
+
for (i = 0; i < argc; i++)
encode(argv[i], subbands, bitpool, joint, dualchannel,
- snr, blocks);
+ snr, blocks, msbc);

return 0;
}
--
1.7.9.5


2012-09-27 14:44:28

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 5/5] update sbcinfo for msbc

---
src/sbcinfo.c | 51 ++++++++++++++++++++++++++++++++++++---------------
1 file changed, 36 insertions(+), 15 deletions(-)

diff --git a/src/sbcinfo.c b/src/sbcinfo.c
index 8cfb54a..52ca458 100644
--- a/src/sbcinfo.c
+++ b/src/sbcinfo.c
@@ -61,12 +61,11 @@ struct sbc_frame_hdr {
#error "Unknown byte order"
#endif

-static int calc_frame_len(struct sbc_frame_hdr *hdr)
+static int calc_frame_len(struct sbc_frame_hdr *hdr, int nrof_blocks)
{
- int tmp, nrof_subbands, nrof_blocks;
+ int tmp, nrof_subbands;

nrof_subbands = (hdr->subbands + 1) * 4;
- nrof_blocks = (hdr->blocks + 1) * 4;

switch (hdr->channel_mode) {
case 0x00:
@@ -89,13 +88,12 @@ static int calc_frame_len(struct sbc_frame_hdr *hdr)
return (nrof_subbands + ((tmp + 7) / 8));
}

-static double calc_bit_rate(struct sbc_frame_hdr *hdr)
+static double calc_bit_rate(struct sbc_frame_hdr *hdr, int nrof_blocks)
{
- int nrof_subbands, nrof_blocks;
+ int nrof_subbands;
double f;

nrof_subbands = (hdr->subbands + 1) * 4;
- nrof_blocks = (hdr->blocks + 1) * 4;

switch (hdr->sampling_frequency) {
case 0:
@@ -114,7 +112,7 @@ static double calc_bit_rate(struct sbc_frame_hdr *hdr)
return 0;
}

- return ((8 * (calc_frame_len(hdr) + 4) * f) /
+ return ((8 * (calc_frame_len(hdr, nrof_blocks) + 4) * f) /
(nrof_subbands * nrof_blocks));
}

@@ -175,7 +173,7 @@ static int analyze_file(char *filename)
double rate;
int bitpool[SIZE], frame_len[SIZE];
int subbands, blocks, freq, method;
- int n, p1, p2, fd, size, num;
+ int n, p1, p2, fd, size, num, msbc;
ssize_t len;
unsigned int count;

@@ -191,17 +189,30 @@ static int analyze_file(char *filename)
fd = fileno(stdin);

len = __read(fd, &hdr, sizeof(hdr));
- if (len != sizeof(hdr) || hdr.syncword != 0x9c) {
+ if (len != sizeof(hdr) || !(hdr.syncword == 0x9c ||
+ hdr.syncword == 0xad)) {
fprintf(stderr, "Not a SBC audio file\n");
return -1;
}
+ msbc = (hdr.syncword == 0xad) ? 1 : 0;
+
+ if (msbc) {
+ hdr.subbands = 1; /* 8 */
+ hdr.sampling_frequency = 0x00; /* 16000 */
+ hdr.allocation_method = 0; /* Loudness */
+ hdr.bitpool = 26;
+ hdr.channel_mode = 0x00; /* Mono */
+
+ blocks = 15;
+ } else {
+ blocks = (hdr.blocks + 1) * 4;
+ }

subbands = (hdr.subbands + 1) * 4;
- blocks = (hdr.blocks + 1) * 4;
freq = hdr.sampling_frequency;
method = hdr.allocation_method;

- count = calc_frame_len(&hdr);
+ count = calc_frame_len(&hdr, blocks);

bitpool[0] = hdr.bitpool;
frame_len[0] = count + 4;
@@ -213,7 +224,7 @@ static int analyze_file(char *filename)

if (lseek(fd, 0, SEEK_SET) < 0) {
num = 1;
- rate = calc_bit_rate(&hdr);
+ rate = calc_bit_rate(&hdr, blocks);
while (count) {
size = count > sizeof(buf) ? sizeof(buf) : count;
len = __read(fd, buf, size);
@@ -237,14 +248,23 @@ static int analyze_file(char *filename)
if (len == 0)
break;

- if ((size_t) len < sizeof(hdr) || hdr.syncword != 0x9c) {
+ if ((size_t) len < sizeof(hdr) || !(hdr.syncword == 0x9c ||
+ hdr.syncword == 0xad)) {
fprintf(stderr, "Corrupted SBC stream "
"(len %zd syncword 0x%02x)\n",
len, hdr.syncword);
break;
}

- count = calc_frame_len(&hdr);
+ if (msbc) {
+ hdr.subbands = 1; /* 8 */
+ hdr.sampling_frequency = 0x00; /* 16000 */
+ hdr.allocation_method = 0; /* Loudness */
+ hdr.bitpool = 26;
+ hdr.channel_mode = 0x00; /* Mono */
+ }
+
+ count = calc_frame_len(&hdr, blocks);
len = count + 4;

p1 = -1;
@@ -273,10 +293,11 @@ static int analyze_file(char *filename)
count -= len;
}

- rate += calc_bit_rate(&hdr);
+ rate += calc_bit_rate(&hdr, blocks);
num++;
}

+ printf("mSBC \t\t%d\n", msbc);
printf("Subbands\t\t%d\n", subbands);
printf("Block length\t\t%d\n", blocks);
printf("Sampling frequency\t%s\n", freq2str(freq));
--
1.7.9.5


2012-09-27 14:44:26

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 3/5] update sbcdec for msbc

---
src/sbcdec.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/sbcdec.c b/src/sbcdec.c
index 0077a82..37d2e98 100644
--- a/src/sbcdec.c
+++ b/src/sbcdec.c
@@ -44,7 +44,7 @@

static int verbose = 0;

-static void decode(char *filename, char *output, int tofile)
+static void decode(char *filename, char *output, int tofile, int msbc)
{
unsigned char buf[BUF_SIZE], *stream;
struct stat st;
@@ -98,7 +98,7 @@ static void decode(char *filename, char *output, int tofile)
goto free;
}

- sbc_init(&sbc, 0L);
+ sbc_init(&sbc, msbc ? SBC_MSBC : 0L);
sbc.endian = SBC_BE;

framelen = sbc_decode(&sbc, stream, streamlen, buf, sizeof(buf), &len);
@@ -228,14 +228,16 @@ static void usage(void)

printf("Options:\n"
"\t-h, --help Display help\n"
- "\t-v, --verbose Verbose mode\n"
"\t-d, --device <dsp> Sound device\n"
+ "\t-v, --verbose Verbose mode\n"
+ "\t-m, --msbc mSBC codec\n"
"\t-f, --file <file> Decode to a file\n"
"\n");
}

static struct option main_options[] = {
{ "help", 0, 0, 'h' },
+ { "msbc", 0, 0, 'm' },
{ "device", 1, 0, 'd' },
{ "verbose", 0, 0, 'v' },
{ "file", 1, 0, 'f' },
@@ -246,8 +248,9 @@ int main(int argc, char *argv[])
{
char *output = NULL;
int i, opt, tofile = 0;
+ int msbc = 0;

- while ((opt = getopt_long(argc, argv, "+hvd:f:",
+ while ((opt = getopt_long(argc, argv, "+hmvd:f:",
main_options, NULL)) != -1) {
switch(opt) {
case 'h':
@@ -258,6 +261,10 @@ int main(int argc, char *argv[])
verbose = 1;
break;

+ case 'm':
+ msbc = 1;
+ break;
+
case 'd':
free(output);
output = strdup(optarg);
@@ -285,7 +292,7 @@ int main(int argc, char *argv[])
}

for (i = 0; i < argc; i++)
- decode(argv[i], output ? output : "/dev/dsp", tofile);
+ decode(argv[i], output ? output : "/dev/dsp", tofile, msbc);

free(output);

--
1.7.9.5


2012-09-27 14:44:24

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 1/5] Add msbc encoding and decoding flag

Also enable 15 blocks support using stdc sbc primitives
---
Makefile.am | 1 +
sbc/sbc.c | 123 +++++++++--------
sbc/sbc.h | 3 +
sbc/sbc_primitives.c | 8 +-
sbc/sbc_primitives.h | 7 +-
sbc/sbc_primitives_stdc.c | 321 +++++++++++++++++++++++++++++++++++++++++++++
sbc/sbc_primitives_stdc.h | 36 +++++
7 files changed, 443 insertions(+), 56 deletions(-)
create mode 100644 sbc/sbc_primitives_stdc.c
create mode 100644 sbc/sbc_primitives_stdc.h

diff --git a/Makefile.am b/Makefile.am
index cad6a3b..75e3a4a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,6 +14,7 @@ sbc_headers = sbc/sbc.h

sbc_sources = sbc/sbc.c sbc/sbc_private.h sbc/sbc_math.h sbc/sbc_tables.h \
sbc/sbc_primitives.h sbc/sbc_primitives.c \
+ sbc/sbc_primitives_stdc.h sbc/sbc_primitives_stdc.c \
sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
diff --git a/sbc/sbc.c b/sbc/sbc.c
index f0c77c7..7e4faa0 100644
--- a/sbc/sbc.c
+++ b/sbc/sbc.c
@@ -6,6 +6,7 @@
* Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
* Copyright (C) 2004-2005 Henryk Ploetz <[email protected]>
* Copyright (C) 2005-2008 Brad Midgley <[email protected]>
+ * Copyright (C) 2012 Intel Corporation
*
*
* This library is free software; you can redistribute it and/or
@@ -51,6 +52,17 @@
#include "sbc_primitives.h"

#define SBC_SYNCWORD 0x9C
+#define MSBC_SYNCWORD 0xAD
+#define SBC_BLOCKS(sbc, blocks) (((sbc)->flags & SBC_MSBC) \
+ ? MSBC_BLOCKS : (blocks))
+
+#define MSBC_BLOCKMODE SBC_BLK_16
+#define MSBC_BLOCKS 15
+#define MSBC_BITPOOL 26
+#define MSBC_SUBBAND_MODE 1
+#define MSBC_SUBBANDS 8
+#define MSBC_CHANNEL 1
+#define MSBC_ALLOCATION SBC_AM_LOUDNESS

/* This structure contains an unpacked SBC frame.
Yes, there is probably quite some unused space herein */
@@ -373,9 +385,11 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
* -2 Sync byte incorrect
* -3 CRC8 incorrect
* -4 Bitpool value out of bounds
+ * -5 msbc reserved byte 1 not 0
+ * -6 msbc reserved byte 2 not 0
*/
-static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
- size_t len)
+static int sbc_unpack_frame(sbc_t *sbc, const uint8_t *data,
+ struct sbc_frame *frame, size_t len)
{
unsigned int consumed;
/* Will copy the parts of the header that are relevant to crc
@@ -413,6 +427,8 @@ static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
frame->blocks = 16;
break;
}
+ if (sbc->flags & SBC_MSBC)
+ frame->blocks = MSBC_BLOCKS;

frame->mode = (data[1] >> 2) & 0x03;
switch (frame->mode) {
@@ -690,13 +706,13 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
for (ch = 0; ch < frame->channels; ch++) {
x = &state->X[ch][state->position - 16 +
frame->blocks * 4];
- for (blk = 0; blk < frame->blocks; blk += 4) {
+ for (blk = 0; blk < frame->blocks; blk += state->inc) {
state->sbc_analyze_4b_4s(
x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- x -= 16;
+ x -= 4 * state->inc;
}
}
return frame->blocks * 4;
@@ -705,13 +721,13 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
for (ch = 0; ch < frame->channels; ch++) {
x = &state->X[ch][state->position - 32 +
frame->blocks * 8];
- for (blk = 0; blk < frame->blocks; blk += 4) {
+ for (blk = 0; blk < frame->blocks; blk += state->inc) {
state->sbc_analyze_4b_8s(
x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- x -= 32;
+ x -= 8 * state->inc;
}
}
return frame->blocks * 8;
@@ -764,10 +780,9 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
* -99 not implemented
*/

-static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
- struct sbc_frame *frame, size_t len,
- int frame_subbands, int frame_channels,
- int joint)
+static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(sbc_t *sbc,
+ uint8_t *data, struct sbc_frame *frame, size_t len,
+ int frame_subbands, int frame_channels, int joint)
{
/* Bitstream writer starts from the fourth byte */
uint8_t *data_ptr = data + 4;
@@ -785,37 +800,37 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
uint32_t levels[2][8]; /* levels are derived from that */
uint32_t sb_sample_delta[2][8];

- data[0] = SBC_SYNCWORD;
+ data[0] = SBC_SYNCWORD;

- data[1] = (frame->frequency & 0x03) << 6;
+ data[1] = (frame->frequency & 0x03) << 6;

- data[1] |= (frame->block_mode & 0x03) << 4;
+ data[1] |= (frame->block_mode & 0x03) << 4;

- data[1] |= (frame->mode & 0x03) << 2;
+ data[1] |= (frame->mode & 0x03) << 2;

- data[1] |= (frame->allocation & 0x01) << 1;
+ data[1] |= (frame->allocation & 0x01) << 1;

- switch (frame_subbands) {
- case 4:
- /* Nothing to do */
- break;
- case 8:
- data[1] |= 0x01;
- break;
- default:
- return -4;
- break;
- }
+ switch (frame_subbands) {
+ case 4:
+ /* Nothing to do */
+ break;
+ case 8:
+ data[1] |= 0x01;
+ break;
+ default:
+ return -4;
+ break;
+ }

- data[2] = frame->bitpool;
+ data[2] = frame->bitpool;

- if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > frame_subbands << 4)
- return -5;
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > frame_subbands << 4)
+ return -5;

- if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > frame_subbands << 5)
- return -5;
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > frame_subbands << 5)
+ return -5;

/* Can't fill in crc yet */

@@ -881,33 +896,33 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
return data_ptr - data;
}

-static ssize_t sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len,
- int joint)
+static ssize_t sbc_pack_frame(sbc_t *sbc, uint8_t *data,
+ struct sbc_frame *frame, size_t len, int joint)
{
if (frame->subbands == 4) {
if (frame->channels == 1)
return sbc_pack_frame_internal(
- data, frame, len, 4, 1, joint);
+ sbc, data, frame, len, 4, 1, joint);
else
return sbc_pack_frame_internal(
- data, frame, len, 4, 2, joint);
+ sbc, data, frame, len, 4, 2, joint);
} else {
if (frame->channels == 1)
return sbc_pack_frame_internal(
- data, frame, len, 8, 1, joint);
+ sbc, data, frame, len, 8, 1, joint);
else
return sbc_pack_frame_internal(
- data, frame, len, 8, 2, joint);
+ sbc, data, frame, len, 8, 2, joint);
}
}

-static void sbc_encoder_init(struct sbc_encoder_state *state,
+static void sbc_encoder_init(int msbc, struct sbc_encoder_state *state,
const struct sbc_frame *frame)
{
memset(&state->X, 0, sizeof(state->X));
state->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7;

- sbc_init_primitives(state);
+ sbc_init_primitives(msbc, state);
}

struct sbc_priv {
@@ -919,6 +934,7 @@ struct sbc_priv {

static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
{
+ sbc->flags = flags;
sbc->frequency = SBC_FREQ_44100;
sbc->mode = SBC_MODE_STEREO;
sbc->subbands = SBC_SB_8;
@@ -971,7 +987,7 @@ SBC_EXPORT ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,

priv = sbc->priv;

- framelen = sbc_unpack_frame(input, &priv->frame, input_len);
+ framelen = sbc_unpack_frame(sbc, input, &priv->frame, input_len);

if (!priv->init) {
sbc_decoder_init(&priv->dec_state, &priv->frame);
@@ -980,7 +996,7 @@ SBC_EXPORT ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
sbc->frequency = priv->frame.frequency;
sbc->mode = priv->frame.mode;
sbc->subbands = priv->frame.subband_mode;
- sbc->blocks = priv->frame.block_mode;
+ sbc->blocks = SBC_BLOCKS(sbc, priv->frame.block_mode);
sbc->allocation = priv->frame.allocation;
sbc->bitpool = priv->frame.bitpool;

@@ -1054,12 +1070,13 @@ SBC_EXPORT ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
priv->frame.subband_mode = sbc->subbands;
priv->frame.subbands = sbc->subbands ? 8 : 4;
priv->frame.block_mode = sbc->blocks;
- priv->frame.blocks = 4 + (sbc->blocks * 4);
+ priv->frame.blocks = SBC_BLOCKS(sbc, 4 + (sbc->blocks * 4));
priv->frame.bitpool = sbc->bitpool;
priv->frame.codesize = sbc_get_codesize(sbc);
priv->frame.length = sbc_get_frame_length(sbc);

- sbc_encoder_init(&priv->enc_state, &priv->frame);
+ sbc_encoder_init(sbc->flags & SBC_MSBC,
+ &priv->enc_state, &priv->frame);
priv->init = 1;
} else if (priv->frame.bitpool != sbc->bitpool) {
priv->frame.length = sbc_get_frame_length(sbc);
@@ -1102,13 +1119,15 @@ SBC_EXPORT ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
int j = priv->enc_state.sbc_calc_scalefactors_j(
priv->frame.sb_sample_f, priv->frame.scale_factor,
priv->frame.blocks, priv->frame.subbands);
- framelen = sbc_pack_frame(output, &priv->frame, output_len, j);
+ framelen = sbc_pack_frame(sbc, output,
+ &priv->frame, output_len, j);
} else {
priv->enc_state.sbc_calc_scalefactors(
priv->frame.sb_sample_f, priv->frame.scale_factor,
priv->frame.blocks, priv->frame.channels,
priv->frame.subbands);
- framelen = sbc_pack_frame(output, &priv->frame, output_len, 0);
+ framelen = sbc_pack_frame(sbc, output,
+ &priv->frame, output_len, 0);
}

if (written)
@@ -1138,7 +1157,7 @@ SBC_EXPORT size_t sbc_get_frame_length(sbc_t *sbc)
return priv->frame.length;

subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
+ blocks = SBC_BLOCKS(sbc, 4 + (sbc->blocks * 4));
channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
bitpool = sbc->bitpool;
@@ -1162,10 +1181,10 @@ SBC_EXPORT unsigned sbc_get_frame_duration(sbc_t *sbc)
priv = sbc->priv;
if (!priv->init) {
subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
+ blocks = SBC_BLOCKS(sbc, 4 + (sbc->blocks * 4));
} else {
subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
+ blocks = SBC_BLOCKS(sbc, priv->frame.blocks);
}

switch (sbc->frequency) {
@@ -1199,11 +1218,11 @@ SBC_EXPORT size_t sbc_get_codesize(sbc_t *sbc)
priv = sbc->priv;
if (!priv->init) {
subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
+ blocks = SBC_BLOCKS(sbc, 4 + (sbc->blocks * 4));
channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
} else {
subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
+ blocks = SBC_BLOCKS(sbc, priv->frame.blocks);
channels = priv->frame.channels;
}

diff --git a/sbc/sbc.h b/sbc/sbc.h
index bbd45da..3511119 100644
--- a/sbc/sbc.h
+++ b/sbc/sbc.h
@@ -64,6 +64,9 @@ extern "C" {
#define SBC_LE 0x00
#define SBC_BE 0x01

+/* Additional features */
+#define SBC_MSBC 0x01
+
struct sbc_struct {
unsigned long flags;

diff --git a/sbc/sbc_primitives.c b/sbc/sbc_primitives.c
index ad780d0..1ed61d1 100644
--- a/sbc/sbc_primitives.c
+++ b/sbc/sbc_primitives.c
@@ -32,6 +32,7 @@
#include "sbc_tables.h"

#include "sbc_primitives.h"
+#include "sbc_primitives_stdc.h"
#include "sbc_primitives_mmx.h"
#include "sbc_primitives_iwmmxt.h"
#include "sbc_primitives_neon.h"
@@ -519,8 +520,10 @@ static int sbc_calc_scalefactors_j(
/*
* Detect CPU features and setup function pointers
*/
-void sbc_init_primitives(struct sbc_encoder_state *state)
+void sbc_init_primitives(int msbc, struct sbc_encoder_state *state)
{
+ state->inc = 4;
+
/* Default implementation for analyze functions */
state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_simd;
state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_simd;
@@ -551,4 +554,7 @@ void sbc_init_primitives(struct sbc_encoder_state *state)
#ifdef SBC_BUILD_WITH_NEON_SUPPORT
sbc_init_primitives_neon(state);
#endif
+
+ if (msbc)
+ sbc_init_primitives_stdc(state);
}
diff --git a/sbc/sbc_primitives.h b/sbc/sbc_primitives.h
index 17ad4f7..22778ff 100644
--- a/sbc/sbc_primitives.h
+++ b/sbc/sbc_primitives.h
@@ -38,12 +38,13 @@

struct sbc_encoder_state {
int position;
+ int inc;
int16_t SBC_ALIGNED X[2][SBC_X_BUFFER_SIZE];
/* Polyphase analysis filter for 4 subbands configuration,
- * it handles 4 blocks at once */
+ * it handles "inc" blocks at once */
void (*sbc_analyze_4b_4s)(int16_t *x, int32_t *out, int out_stride);
/* Polyphase analysis filter for 8 subbands configuration,
- * it handles 4 blocks at once */
+ * it handles "inc" blocks at once */
void (*sbc_analyze_4b_8s)(int16_t *x, int32_t *out, int out_stride);
/* Process input data (deinterleave, endian conversion, reordering),
* depending on the number of subbands and input data byte order */
@@ -75,6 +76,6 @@ struct sbc_encoder_state {
* of SBC codec. Best implementation is selected based on target CPU
* capabilities.
*/
-void sbc_init_primitives(struct sbc_encoder_state *encoder_state);
+void sbc_init_primitives(int msbc, struct sbc_encoder_state *encoder_state);

#endif
diff --git a/sbc/sbc_primitives_stdc.c b/sbc/sbc_primitives_stdc.c
new file mode 100644
index 0000000..02ad8eb
--- /dev/null
+++ b/sbc/sbc_primitives_stdc.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2004-2005 Henryk Ploetz <[email protected]>
+ * Copyright (C) 2005-2006 Brad Midgley <[email protected]>
+ * Copyright (C) 2012 Intel Corporation
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives.h"
+#include "sbc_primitives_stdc.h"
+
+/*
+ * stdc optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_STDC_SUPPORT
+
+/*
+ * A standard C code of analysis filter.
+ */
+static inline void sbc_analyze_four_stdc(const int16_t *in, int32_t *out)
+{
+ FIXED_A t1[4];
+ FIXED_T t2[4];
+ int i = 0, hop = 0;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 40; hop += 8) {
+ t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed4[hop];
+ t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed4[hop + 1];
+ t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed4[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed4[hop + 3];
+ t1[0] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed4[hop + 4];
+ t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed4[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed4[hop + 7];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+ /* do the cos transform */
+ for (i = 0, hop = 0; i < 4; hop += 8, i++) {
+ out[i] = ((FIXED_A) t2[0] * cos_table_fixed_4[0 + hop] +
+ (FIXED_A) t2[1] * cos_table_fixed_4[1 + hop] +
+ (FIXED_A) t2[2] * cos_table_fixed_4[2 + hop] +
+ (FIXED_A) t2[3] * cos_table_fixed_4[5 + hop]) >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ }
+}
+
+static inline void sbc_analyze_eight_stdc(const int16_t *in, int32_t *out)
+{
+ FIXED_A t1[8];
+ FIXED_T t2[8];
+ int i, hop;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 80; hop += 16) {
+ t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed8[hop];
+ t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed8[hop + 1];
+ t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed8[hop + 2];
+ t1[3] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed8[hop + 3];
+ t1[4] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed8[hop + 4];
+ t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed8[hop + 5];
+ t1[2] += (FIXED_A) in[hop + 6] * _sbc_proto_fixed8[hop + 6];
+ t1[1] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed8[hop + 7];
+ t1[0] += (FIXED_A) in[hop + 8] * _sbc_proto_fixed8[hop + 8];
+ t1[5] += (FIXED_A) in[hop + 9] * _sbc_proto_fixed8[hop + 9];
+ t1[6] += (FIXED_A) in[hop + 10] * _sbc_proto_fixed8[hop + 10];
+ t1[7] += (FIXED_A) in[hop + 11] * _sbc_proto_fixed8[hop + 11];
+ t1[7] += (FIXED_A) in[hop + 13] * _sbc_proto_fixed8[hop + 13];
+ t1[6] += (FIXED_A) in[hop + 14] * _sbc_proto_fixed8[hop + 14];
+ t1[5] += (FIXED_A) in[hop + 15] * _sbc_proto_fixed8[hop + 15];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+ t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+ t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+ t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+ t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+ /* do the cos transform */
+ for (i = 0, hop = 0; i < 8; hop += 16, i++) {
+ out[i] = ((FIXED_A) t2[0] * cos_table_fixed_8[0 + hop] +
+ (FIXED_A) t2[1] * cos_table_fixed_8[1 + hop] +
+ (FIXED_A) t2[2] * cos_table_fixed_8[2 + hop] +
+ (FIXED_A) t2[3] * cos_table_fixed_8[3 + hop] +
+ (FIXED_A) t2[4] * cos_table_fixed_8[4 + hop] +
+ (FIXED_A) t2[5] * cos_table_fixed_8[9 + hop] +
+ (FIXED_A) t2[6] * cos_table_fixed_8[10 + hop] +
+ (FIXED_A) t2[7] * cos_table_fixed_8[11 + hop]) >>
+ (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+ }
+}
+
+static inline void sbc_analyze_4b_4s_stdc(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_stdc(x + 0, out);
+}
+
+static inline void sbc_analyze_4b_8s_stdc(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_stdc(x + 0, out);
+}
+
+static inline int16_t unaligned16_be(const uint8_t *ptr)
+{
+ return (int16_t) ((ptr[0] << 8) | ptr[1]);
+}
+
+static inline int16_t unaligned16_le(const uint8_t *ptr)
+{
+ return (int16_t) (ptr[0] | (ptr[1] << 8));
+}
+
+/*
+ * Internal helper functions for input data processing. In order to get
+ * optimal performance, it is important to have "nsamples", "nchannels"
+ * and "big_endian" arguments used with this inline function as compile
+ * time constants.
+ */
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_stdc(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 40], &X[0][position],
+ 36 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 40], &X[1][position],
+ 36 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 40;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy audio samples */
+ while ((nsamples -= 1) >= 0) {
+ position -= 1;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 0 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[1] = PCM(1 + 0 * nchannels);
+ }
+ pcm += 2 * nchannels;
+ }
+
+
+ #undef PCM
+
+ return position;
+}
+
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s8_stdc(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 72], &X[0][position],
+ 72 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 72], &X[1][position],
+ 72 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 72;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy audio samples */
+ while ((nsamples -= 1) >= 0) {
+ position -= 1;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 0 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[1] = PCM(1 + 0 * nchannels);
+ }
+ pcm += 2 * nchannels;
+ }
+
+ #undef PCM
+ return position;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s_le_stdc(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_stdc(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s4_stdc(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_4s_be_stdc(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_stdc(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s4_stdc(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+static int sbc_enc_process_input_8s_le_stdc(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_stdc(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s8_stdc(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_8s_be_stdc(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_stdc(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s8_stdc(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+
+void sbc_init_primitives_stdc(struct sbc_encoder_state *state)
+{
+ state->inc = 1;
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_stdc;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_stdc;
+
+ /* Default implementation for input reordering / deinterleaving */
+ state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le_stdc;
+ state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be_stdc;
+ state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le_stdc;
+ state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be_stdc;
+
+ state->implementation_info = "stdc";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_stdc.h b/sbc/sbc_primitives_stdc.h
new file mode 100644
index 0000000..e313047
--- /dev/null
+++ b/sbc/sbc_primitives_stdc.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>
+ * Copyright (C) 2004-2005 Henryk Ploetz <[email protected]>
+ * Copyright (C) 2005-2006 Brad Midgley <[email protected]>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_STDC_H
+#define __SBC_PRIMITIVES_STDC_H
+
+#include "sbc_primitives.h"
+
+#define SBC_BUILD_WITH_STDC_SUPPORT
+
+void sbc_init_primitives_stdc(struct sbc_encoder_state *encoder_state);
+
+#endif
--
1.7.9.5


2012-09-27 14:44:25

by Frédéric DALLEAU

[permalink] [raw]
Subject: [PATCH 2/5] Add support for mSBC frame header

mSBC modifies header so that it contains: OxAD 0x00 0x00.
The first bytes allows to distinguish mSBC packets from standard SBC
packets used in A2DP. The two zero bytes are reserved for future definition.
---
sbc/sbc.c | 104 +++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 63 insertions(+), 41 deletions(-)

diff --git a/sbc/sbc.c b/sbc/sbc.c
index 7e4faa0..131755b 100644
--- a/sbc/sbc.c
+++ b/sbc/sbc.c
@@ -407,55 +407,71 @@ static int sbc_unpack_frame(sbc_t *sbc, const uint8_t *data,
if (len < 4)
return -1;

- if (data[0] != SBC_SYNCWORD)
- return -2;
-
- frame->frequency = (data[1] >> 6) & 0x03;
+ if (sbc->flags & SBC_MSBC) {
+ if (data[0] != MSBC_SYNCWORD)
+ return -2;
+ if (data[1] != 0)
+ return -5;
+ if (data[2] != 0)
+ return -6;

- frame->block_mode = (data[1] >> 4) & 0x03;
- switch (frame->block_mode) {
- case SBC_BLK_4:
- frame->blocks = 4;
- break;
- case SBC_BLK_8:
- frame->blocks = 8;
- break;
- case SBC_BLK_12:
- frame->blocks = 12;
- break;
- case SBC_BLK_16:
- frame->blocks = 16;
- break;
- }
- if (sbc->flags & SBC_MSBC)
+ frame->frequency = SBC_FREQ_16000;
+ frame->block_mode = SBC_BLK_4;
frame->blocks = MSBC_BLOCKS;
-
- frame->mode = (data[1] >> 2) & 0x03;
- switch (frame->mode) {
- case MONO:
+ frame->allocation = LOUDNESS;
+ frame->mode = MONO;
frame->channels = 1;
- break;
- case DUAL_CHANNEL: /* fall-through */
- case STEREO:
- case JOINT_STEREO:
- frame->channels = 2;
- break;
- }
+ frame->subband_mode = 1;
+ frame->subbands = 8;
+ frame->bitpool = 26;
+ } else {
+ if (data[0] != SBC_SYNCWORD)
+ return -2;
+
+ frame->frequency = (data[1] >> 6) & 0x03;
+ frame->block_mode = (data[1] >> 4) & 0x03;
+ switch (frame->block_mode) {
+ case SBC_BLK_4:
+ frame->blocks = 4;
+ break;
+ case SBC_BLK_8:
+ frame->blocks = 8;
+ break;
+ case SBC_BLK_12:
+ frame->blocks = 12;
+ break;
+ case SBC_BLK_16:
+ frame->blocks = 16;
+ break;
+ }
+
+ frame->mode = (data[1] >> 2) & 0x03;
+ switch (frame->mode) {
+ case MONO:
+ frame->channels = 1;
+ break;
+ case DUAL_CHANNEL: /* fall-through */
+ case STEREO:
+ case JOINT_STEREO:
+ frame->channels = 2;
+ break;
+ }

- frame->allocation = (data[1] >> 1) & 0x01;
+ frame->allocation = (data[1] >> 1) & 0x01;

- frame->subband_mode = (data[1] & 0x01);
- frame->subbands = frame->subband_mode ? 8 : 4;
+ frame->subband_mode = (data[1] & 0x01);
+ frame->subbands = frame->subband_mode ? 8 : 4;

- frame->bitpool = data[2];
+ frame->bitpool = data[2];

- if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > 16 * frame->subbands)
- return -4;
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > 16 * frame->subbands)
+ return -4;

- if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > 32 * frame->subbands)
- return -4;
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > 32 * frame->subbands)
+ return -4;
+ }

/* data[3] is crc, we're checking it later */

@@ -800,6 +816,11 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(sbc_t *sbc,
uint32_t levels[2][8]; /* levels are derived from that */
uint32_t sb_sample_delta[2][8];

+ if (sbc->flags & SBC_MSBC) {
+ data[0] = MSBC_SYNCWORD;
+ data[1] = 0;
+ data[2] = 0;
+ } else {
data[0] = SBC_SYNCWORD;

data[1] = (frame->frequency & 0x03) << 6;
@@ -831,6 +852,7 @@ static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(sbc_t *sbc,
if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
frame->bitpool > frame_subbands << 5)
return -5;
+ }

/* Can't fill in crc yet */

--
1.7.9.5


2012-10-30 02:18:50

by Siarhei Siamashka

[permalink] [raw]
Subject: Re: [PATCH 0/5] mSBC tests

On Fri, 28 Sep 2012 17:05:42 +0200
Marcel Holtmann <[email protected]> wrote:

> > > Work done from the spec, not tested with any reference encoder.
> >
> > Does a reference mSBC encoder exist somewhere? Or any ideas how to test
> > the new code with something other than bluez just to be sure that it's
> > correct?
>
> I looked at the Bluetooth SIG website and couldn't find a reference
> encoder or sample files. The old SBC reference encoder and decoder don't
> seem to support other block sizes.
>
> SBC Encoder LIB Version 1.5
> Copyright (c) 2002 Philips Consumer Electronics, ASA Labs
>
> Usage:
> sbc_encoder [-jsv] [-lblk_len] [-nsubbands] [-p] [-rrate] [-ooutputfile] inputfile
> [-s] use the stereo mode for stereo signals
> [-v] verbose mode
> [-j] allow the use of joint coding for stereo signals
> [-lblk_len] blk_len specifies the APCM block length, out of [4,8,12,16]
> [-nsubbands] subbands specifies the number of subbands, out of [4,8]
> [-p] a simple psycho acoustic model is used
> [-rrate] specifies the bit rate in bps
> [-ooutputfile] specifies the name of the bitstream output file
> inputfile specifies the audio input file, the major audio formats are supported
>
>
> SBC Decoder LIB Version 1.5
> Copyright (c) 2002 Philips Consumer Electronics, ASA Labs
>
> Usage:
> sbc_decoder [-v] [-ooutputfile] [-pstartpos] inputfile
> [-v] verbose mode
> [-pstartpos] startpos specifies the byte offset to start with decoding
> [-ooutputfile] specifies the name of the audio output file
> inputfile specifies the name of the bitstream input file
>
> And the sample files are all from 2001. We might have to create our own
> ones and our own naive and stupid reference encoder/decoder.

Hi Marcel,

As the first step, some sort of automated testing ("make check") for
verifying sbc encoding/decoding correctness might be useful.

So far I have been using the following quickly hacked script. It just
takes a directory with *.au files, encodes them using different
settings and provides a log with md5 hashes which can be compared with
the reference log:

#!/usr/bin/env ruby

testfiles = []
Dir.new("testsamples").each {|file|
next if not file =~ /\.au$/
testfiles.push("testsamples/" + file)
}

if File.exists?("sbc-test.ok") then
fh = File.open("sbc-test.ok", "r")
okdata = fh.read()
fh.close()
end

outstring = ""

testfiles.sort!

testfiles.each {|file|

["-b32", "-b64", "-b128", "-b255"].each {|br|
["-s4", "-s8"].each {|sb|
["-S", ""].each {|snr|
["-j", ""].each {|j|
["-d", ""].each {|d|
["-B4", "-B8", "-B12", "-B16"].each {|blk|
outstring += sprintf("%s #{file} (#{br} #{sb} #{snr} #{j} #{d} #{blk})\n",
`./sbcenc #{br} #{sb} #{snr} #{j} #{d} #{blk} #{file} 2>/dev/null | md5sum`.strip).gsub(/[ ]+/, " ")
}
}
}
}
}
}
}

if not okdata then
printf("no reference ok data\n")
end

if outstring != okdata then
printf("test failed!\n")
else
printf("test ok\n")
end

fh = File.open("sbc-test.results", "w")
fh.write(outstring)
fh.close()

############################

Though I see this scripts as a dead end, because my media files are a
bit too large for adding to a git repository and these files are
all copyrighted. Some of them have a permissive license (big buck
bunny soundtrack), some of them do not.

Anyway, based on the fact that we use a fixed point encoder for sbc and
its output is deterministic, we can just encode some pseudo random noise
data and compare the checksums/hashes of the encoded results with some
reference values.

--
Best regards,
Siarhei Siamashka