Skip to content

Commit 5a2f1ec

Browse files
committed
New version 0.1.0 which fixes decoding delay
New API function aptx_encode_finish() flushes encoding buffer and adds last 90 encoded samples, so caller has ability to encode whole input stream. Function aptx_decode() was fixed to drop first 90 decoded samples which are used just for predictor and contain noise.
1 parent 5eb6ce6 commit 5a2f1ec

File tree

5 files changed

+92
-26
lines changed

5 files changed

+92
-26
lines changed

Makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
MAJOR := 0
2-
MINOR := 0
2+
MINOR := 1
33
PATCH := 0
44

55
PREFIX := /usr/local
@@ -11,9 +11,10 @@ LIBNAME := libopenaptx.so
1111
SONAME := $(LIBNAME).$(MAJOR)
1212
FILENAME := $(SONAME).$(MINOR).$(PATCH)
1313

14-
UTILITIES := openaptxenc openaptxdec
14+
UTILITIES := openaptxenc openaptxdec openaptxenc-static openaptxdec-static
1515

1616
HEADERS := openaptx.h
17+
SOURCES := openaptx.c
1718

1819
BUILD := $(FILENAME) $(SONAME) $(LIBNAME) $(UTILITIES)
1920

@@ -27,15 +28,18 @@ install: $(BUILD)
2728
mkdir -p $(DESTDIR)/$(PREFIX)/$(INCDIR)
2829
cp -a $(HEADERS) $(DESTDIR)/$(PREFIX)/$(INCDIR)
2930

30-
$(FILENAME): openaptx.c $(HEADERS)
31-
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -shared -fPIC -Wl,-soname,$(SONAME) -o $@ $<
31+
$(FILENAME): $(SOURCES) $(HEADERS)
32+
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -shared -fPIC -Wl,-soname,$(SONAME) -o $@ $(SOURCES)
3233

3334
$(SONAME): $(FILENAME)
3435
ln -sf $< $@
3536

3637
$(LIBNAME): $(SONAME)
3738
ln -sf $< $@
3839

40+
%-static: %.c $(SOURCES) $(HEADERS)
41+
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -o $@ $< $(SOURCES)
42+
3943
%: %.c $(LIBNAME) $(HEADERS)
4044
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -o $@ $< $(LIBNAME)
4145

openaptx.c

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ enum subbands {
7878

7979
#define NB_FILTERS 2
8080
#define FILTER_TAPS 16
81+
#define LATENCY_SAMPLES 90
8182

8283
struct aptx_filter_signal {
83-
int pos;
84+
uint8_t pos;
8485
int32_t buffer[2*FILTER_TAPS];
8586
};
8687

@@ -124,8 +125,10 @@ struct aptx_channel {
124125
} ;
125126

126127
struct aptx_context {
127-
int hd;
128-
int32_t sync_idx;
128+
uint8_t hd;
129+
uint8_t sync_idx;
130+
uint8_t encode_remaining;
131+
uint8_t decode_skip_leading;
129132
struct aptx_channel channels[NB_CHANNELS];
130133
};
131134

@@ -900,20 +903,19 @@ static int32_t aptx_quantized_parity(struct aptx_channel *channel)
900903

901904
/* For each sample, ensure that the parity of all subbands of all channels
902905
* is 0 except once every 8 samples where the parity is forced to 1. */
903-
static int aptx_check_parity(struct aptx_channel channels[NB_CHANNELS], int32_t *idx)
906+
static int aptx_check_parity(struct aptx_channel channels[NB_CHANNELS], uint8_t *sync_idx)
904907
{
905908
int32_t parity = aptx_quantized_parity(&channels[LEFT])
906909
^ aptx_quantized_parity(&channels[RIGHT]);
910+
int32_t eighth = *sync_idx == 7;
907911

908-
int eighth = *idx == 7;
909-
*idx = (*idx + 1) & 7;
910-
912+
*sync_idx = (*sync_idx + 1) & 7;
911913
return parity ^ eighth;
912914
}
913915

914-
static void aptx_insert_sync(struct aptx_channel channels[NB_CHANNELS], int32_t *idx)
916+
static void aptx_insert_sync(struct aptx_channel channels[NB_CHANNELS], uint8_t *sync_idx)
915917
{
916-
if (aptx_check_parity(channels, idx)) {
918+
if (aptx_check_parity(channels, sync_idx)) {
917919
int i;
918920
struct aptx_channel *c;
919921
static const int map[] = { 1, 2, 0, 3 };
@@ -1022,6 +1024,10 @@ static int aptx_decode_samples(struct aptx_context *ctx,
10221024
}
10231025

10241026

1027+
int aptx_major = OPENAPTX_MAJOR;
1028+
int aptx_minor = OPENAPTX_MINOR;
1029+
int aptx_patch = OPENAPTX_PATCH;
1030+
10251031
struct aptx_context *aptx_init(int hd)
10261032
{
10271033
struct aptx_context *ctx;
@@ -1043,6 +1049,8 @@ void aptx_reset(struct aptx_context *ctx)
10431049
hd = ctx->hd;
10441050
memset(ctx, 0, sizeof(*ctx));
10451051
ctx->hd = hd;
1052+
ctx->decode_skip_leading = (LATENCY_SAMPLES+3)/4;
1053+
ctx->encode_remaining = (LATENCY_SAMPLES+3)/4;
10461054

10471055
for (chan = 0; chan < NB_CHANNELS; chan++) {
10481056
struct aptx_channel *channel = &ctx->channels[chan];
@@ -1071,7 +1079,7 @@ size_t aptx_encode(struct aptx_context *ctx, const unsigned char *input, size_t
10711079
for (ipos = 0, opos = 0; ipos + 3*NB_CHANNELS*4 <= input_size && opos + sample_size <= output_size; opos += sample_size) {
10721080
for (sample = 0; sample < 4; sample++) {
10731081
for (channel = 0; channel < NB_CHANNELS; channel++, ipos += 3) {
1074-
/* samples need to contain 24bit signed intger stored as 32bit signed integers */
1082+
/* samples need to contain 24bit signed integer stored as 32bit signed integers */
10751083
/* last int8_t --> uint32_t cast propagates sign bit for 32bit integer */
10761084
samples[channel][sample] = ((uint32_t)input[ipos+0] << 0) |
10771085
((uint32_t)input[ipos+1] << 8) |
@@ -1085,6 +1093,31 @@ size_t aptx_encode(struct aptx_context *ctx, const unsigned char *input, size_t
10851093
return ipos;
10861094
}
10871095

1096+
int aptx_encode_finish(struct aptx_context *ctx, unsigned char *output, size_t output_size, size_t *written)
1097+
{
1098+
const int32_t samples[NB_CHANNELS][4] = { };
1099+
int sample_size;
1100+
size_t opos;
1101+
1102+
sample_size = ctx->hd ? 6 : 4;
1103+
1104+
if (ctx->encode_remaining == 0) {
1105+
*written = 0;
1106+
return 1;
1107+
}
1108+
1109+
for (opos = 0; ctx->encode_remaining > 0 && opos + sample_size <= output_size; ctx->encode_remaining--, opos += sample_size)
1110+
aptx_encode_samples(ctx, samples, output + opos);
1111+
1112+
*written = opos;
1113+
1114+
if (ctx->encode_remaining > 0)
1115+
return 0;
1116+
1117+
aptx_reset(ctx);
1118+
return 1;
1119+
}
1120+
10881121
size_t aptx_decode(struct aptx_context *ctx, const unsigned char *input, size_t input_size, unsigned char *output, size_t output_size, size_t *written)
10891122
{
10901123
int32_t samples[NB_CHANNELS][4];
@@ -1097,7 +1130,14 @@ size_t aptx_decode(struct aptx_context *ctx, const unsigned char *input, size_t
10971130
for (ipos = 0, opos = 0; ipos + sample_size <= input_size && opos + 3*NB_CHANNELS*4 <= output_size; ipos += sample_size) {
10981131
if (aptx_decode_samples(ctx, input + ipos, samples))
10991132
break;
1100-
for (sample = 0; sample < 4; sample++) {
1133+
sample = 0;
1134+
if (ctx->decode_skip_leading > 0) {
1135+
ctx->decode_skip_leading--;
1136+
if (ctx->decode_skip_leading > 0)
1137+
continue;
1138+
sample = LATENCY_SAMPLES%4;
1139+
}
1140+
for (; sample < 4; sample++) {
11011141
for (channel = 0; channel < NB_CHANNELS; channel++, opos += 3) {
11021142
/* samples contain 24bit signed integers stored as 32bit signed integers */
11031143
/* we do not need to care about negative integers specially as they have 24. bit set */

openaptx.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,15 @@
2121
#define OPENAPTX_H
2222

2323
#define OPENAPTX_MAJOR 0
24-
#define OPENAPTX_MINOR 0
24+
#define OPENAPTX_MINOR 1
2525
#define OPENAPTX_PATCH 0
2626

27+
#include <stddef.h>
28+
29+
extern int aptx_major;
30+
extern int aptx_minor;
31+
extern int aptx_patch;
32+
2733
struct aptx_context;
2834

2935
/*
@@ -62,6 +68,20 @@ size_t aptx_encode(struct aptx_context *ctx,
6268
size_t output_len,
6369
size_t *written);
6470

71+
/*
72+
* Finish encoding of current stream and reset internal state to be ready for
73+
* encoding new stream. Due to aptX latency, last 90 samples (rounded to 92)
74+
* will be filled by this finish function. When output buffer is too small, this
75+
* function returns zero, fills buffer only partially, does not reset internal
76+
* state and subsequent calls continue filling output buffer. When output buffer
77+
* is large enough, then function returns non-zero value. In both cases into
78+
* written pointer is stored length of encoded samples.
79+
*/
80+
int aptx_encode_finish(struct aptx_context *ctx,
81+
unsigned char *output,
82+
size_t output_len,
83+
size_t *written);
84+
6585
/*
6686
* Decodes aptX audio samples in input buffer with size input_len to sequence
6787
* of raw 24bit signed stereo samples into output buffer with size output_len.
@@ -72,7 +92,8 @@ size_t aptx_encode(struct aptx_context *ctx,
7292
* sequence of 24 bytes in format LLLRRRLLLRRRLLLRRRLLLRRR (L-left, R-right)
7393
* for one aptX sample. Due to aptX parity check it is suggested to provide
7494
* multiple of eight aptX samples, therefore multiple of 8*4 bytes for aptX
75-
* reps. 8*6 bytes for aptX HD.
95+
* and 8*6 bytes for aptX HD. Due to aptX latency, output buffer starts filling
96+
* after 90 samples.
7697
*/
7798
size_t aptx_decode(struct aptx_context *ctx,
7899
const unsigned char *input,

openaptxdec.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ int main(int argc, char *argv[])
4242

4343
for (i = 1; i < argc; ++i) {
4444
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
45-
fprintf(stderr, "aptX decoder utility\n");
45+
fprintf(stderr, "aptX decoder utility %d.%d.%d (using libopenaptx %d.%d.%d)\n", OPENAPTX_MAJOR, OPENAPTX_MINOR, OPENAPTX_PATCH, aptx_major, aptx_minor, aptx_patch);
4646
fprintf(stderr, "\n");
4747
fprintf(stderr, "This utility decodes aptX or aptX HD audio stream\n");
4848
fprintf(stderr, "from stdin to a raw 24 bit signed stereo on stdout\n");
@@ -86,12 +86,12 @@ int main(int argc, char *argv[])
8686
length = fread(input_buffer, 1, 6, stdin);
8787
if (length >= 4 && memcmp(input_buffer, "\x4b\xbf\x4b\xbf", 4) == 0) {
8888
if (hd)
89-
fprintf(stderr, "%s: Input looks like aptX audio stream (not aptX HD), try without --hd\n", argv[0]);
89+
fprintf(stderr, "%s: Input looks like start of aptX audio stream (not aptX HD), try without --hd\n", argv[0]);
9090
} else if (length >= 6 && memcmp(input_buffer, "\x73\xbe\xff\x73\xbe\xff", 6) == 0) {
9191
if (!hd)
92-
fprintf(stderr, "%s: Input looks like aptX HD audio stream, try with --hd\n", argv[0]);
92+
fprintf(stderr, "%s: Input looks like start of aptX HD audio stream, try with --hd\n", argv[0]);
9393
} else {
94-
fprintf(stderr, "%s: Input does not look like aptX nor aptX HD audio stream\n", argv[0]);
94+
fprintf(stderr, "%s: Input does not look like start of aptX nor aptX HD audio stream, trying to synchronize\n", argv[0]);
9595
}
9696

9797
while (length > 0 || !feof(stdin)) {
@@ -140,8 +140,6 @@ int main(int argc, char *argv[])
140140
processed = 1;
141141
written = 0;
142142
}
143-
} else if (length < sample_size) {
144-
fprintf(stderr, "%s: aptX decoding stopped in the middle of the sample\n", argv[0]);
145143
}
146144

147145
if (written > 0) {

openaptxenc.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ int main(int argc, char *argv[])
3838

3939
for (i = 1; i < argc; ++i) {
4040
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
41-
fprintf(stderr, "aptX encoder utility\n");
41+
fprintf(stderr, "aptX encoder utility %d.%d.%d (using libopenaptx %d.%d.%d)\n", OPENAPTX_MAJOR, OPENAPTX_MINOR, OPENAPTX_PATCH, aptx_major, aptx_minor, aptx_patch);
4242
fprintf(stderr, "\n");
4343
fprintf(stderr, "This utility encodes a raw 24 bit signed stereo\n");
4444
fprintf(stderr, "samples from stdin to aptX or aptX HD on stdout\n");
@@ -78,8 +78,6 @@ int main(int argc, char *argv[])
7878
processed = aptx_encode(ctx, input_buffer, length, output_buffer, sizeof(output_buffer), &written);
7979
if (processed != length)
8080
fprintf(stderr, "%s: aptX encoding stopped in the middle of the sample, dropped %u bytes\n", argv[0], (unsigned int)(length-processed));
81-
else if (processed % (8*3*2*4))
82-
fprintf(stderr, "%s: aptX encoding stopped in the middle of the sample\n", argv[0]);
8381
if (fwrite(output_buffer, 1, written, stdout) != written) {
8482
fprintf(stderr, "%s: aptX encoding failed to write encoded data\n", argv[0]);
8583
break;
@@ -88,6 +86,11 @@ int main(int argc, char *argv[])
8886
break;
8987
}
9088

89+
if (aptx_encode_finish(ctx, output_buffer, sizeof(output_buffer), &written)) {
90+
if (fwrite(output_buffer, 1, written, stdout) != written)
91+
fprintf(stderr, "%s: aptX encoding failed to write encoded data\n", argv[0]);
92+
}
93+
9194
aptx_finish(ctx);
9295
return 0;
9396
}

0 commit comments

Comments
 (0)