diff --git a/Makefile b/Makefile index 4a288a4..c16cdfd 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ CFLAGS+=-mfpu=neon -march=armv8-a -mtune=cortex-a53 endif PROG=dump_data test_lpcnet test_vec quant_feat tcodec2_pitch weight tdump tweak_pitch quant_test \ - quant2c diff32 quant_enc quant_dec lpcnet_enc + quant2c diff32 quant_enc quant_dec lpcnet_enc lpcnet_dec all: $(PROG) dump_data_objs := src/dump_data.o src/freq.o src/kiss_fft.o src/pitch.o src/celt_lpc.o src/codec2_pitch.o @@ -83,6 +83,13 @@ lpcnet_enc: $(lpcnet_enc_objs) gcc -o $@ $(CFLAGS) $(lpcnet_enc_objs) -lm -lcodec2 -include $(lpcnet_enc_deps) +lpcnet_dec_objs := src/lpcnet_dec.o src/freq.o src/kiss_fft.o src/celt_lpc.o src/pitch.o src/codec2_pitch.o src/mbest.o src/lpcnet_quant.o \ + src/4stage_pred_vq.o src/lpcnet_dump.o src/lpcnet.o src/nnet.o src/nnet_data.o +lpcnet_dec_deps := $(lpcnet_dec_objs:.o=.d) +lpcnet_dec: $(lpcnet_dec_objs) + gcc -o $@ $(CFLAGS) $(lpcnet_dec_objs) -lm -lcodec2 +-include $(lpcnet_dec_deps) + quant2c_objs:= src/quant2c.o quan2c_deps := $(quant2c_objs:.o=.d) quant2c: $(quant2c_objs) diff --git a/README.md b/README.md index c5ff690..6abaef5 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,21 @@ Fully quantised at (44+8)/0.03 = 1733 bits/s: ```sox -r 16000 ~/Downloads/wianews-2019-01-20.s16 -t raw - trim 200 | ./dump_data --c2pitch --test - - | ./quant_feat -g 0.25 -o 6 -d 3 -w --mbest 5 -q pred_v2_stage1.f32,pred_v2_stage2.f32,pred_v2_stage3.f32,pred_v2_stage4.f32 | ./test_lpcnet - - | aplay -f S16_LE -r 16000``` -Same thing with quantiation code packaged up into library functions. Between quant_enc and quant_dec are 52 bit frames every 30ms: +## Fully quantised encoder/decoder programs + +Same thing as above with quantisation code packaged up into library functions. Between quant_enc and quant_dec are 52 bit frames every 30ms: ```cat ~/Downloads/speech_orig_16k.s16 | ./dump_data --c2pitch --test - - | ./quant_enc | ./quant_dec | ./test_lpcnet - - | aplay -f S16_LE -r 16000``` +Same thing with everything integrated into stand alone encoder and decoder programs: + +```cat ~/Downloads/speech_orig_16k.s16 | ./lpcnet_enc | ./lpcnet_dec | aplay -f S16_LE -r 16000``` + +The bit stream interface is 1 bit/char, as I find that convenient for my digital voice over radio experiments. The decimation rate, number of VQ stages, and a few other parameters can be set as command line options. You'll need the same set of parameters for the encoder as decoder. + +Useful additions would be: + +1. Run time loading of .h5 NN models. +1. A --packed option to pack the quantised bits tightly, which would make the programs useful for storage applications. + + \ No newline at end of file diff --git a/src/lpcnet_dec.c b/src/lpcnet_dec.c new file mode 100644 index 0000000..ab11e96 --- /dev/null +++ b/src/lpcnet_dec.c @@ -0,0 +1,143 @@ +/* + lpcnet_dec.c + Feb 2019 + + LPCNet to bit stream decoder, converts fully quantised bit stream + on stdin (in 1 bit per char format) to 16 kHz signed 16 bit speech + samples on stdout. +*/ + +/* Copyright (c) 2018 Mozilla */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "arch.h" +#include "freq.h" +#include "lpcnet_quant.h" +// NB_FEATURES has a different value in lpcnet.h, need to reconcile some time +#undef NB_FEATURES +#include "lpcnet.h" + +extern int num_stages; +extern float vq[MAX_STAGES*NB_BANDS*MAX_ENTRIES]; +extern int m[MAX_STAGES]; + +int main(int argc, char **argv) { + FILE *fin, *fout; + + /* quantiser defaults */ + + int dec = 3; + float pred = 0.9; + int mbest_survivors = 5; + float weight = 1.0/sqrt(NB_BANDS); + int pitch_bits = 6; + + /* quantiser options */ + + static struct option long_options[] = { + {"decimate", required_argument, 0, 'd'}, + {"numstages", required_argument, 0, 'n'}, + {"pitchquant", required_argument, 0, 'o'}, + {"pred", required_argument, 0, 'p'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + int c; + int opt_index = 0; + + while ((c = getopt_long (argc, argv, "d:n:o:p:v", long_options, &opt_index)) != -1) { + switch (c) { + case 'd': + dec = atoi(optarg); + fprintf(stderr, "dec = %d\n", dec); + break; + case 'n': + num_stages = atoi(optarg); + fprintf(stderr, "%d VQ stages\n", num_stages); + break; + case 'o': + pitch_bits = atoi(optarg); + fprintf(stderr, "pitch quantised to %d bits\n", pitch_bits); + break; + case 'p': + pred = atof(optarg); + fprintf(stderr, "pred = %f\n", pred); + break; + case 'v': + lpcnet_verbose = 1; + break; + default: + fprintf(stderr,"usage: %s [Options]:\n [-d --decimation 1/2/3...]\n", argv[0]); + fprintf(stderr," [-n --numstages]\n [-o --pitchbits nBits]\n"); + fprintf(stderr," [-p --pred predCoff]\n"); + fprintf(stderr," [-v --verbose]\n"); + exit(1); + } + } + + + LPCNET_QUANT *q = lpcnet_quant_create(num_stages, m, vq); + q->weight = weight; q->pred = pred; q->mbest = mbest_survivors; + q->pitch_bits = pitch_bits; q->dec = dec; + lpcnet_quant_compute_bits_per_frame(q); + + fprintf(stderr, "dec: %d pred: %3.2f num_stages: %d mbest: %d bits_per_frame: %d frame: %2d ms bit_rate: %5.2f bits/s", + q->dec, q->pred, q->num_stages, q->mbest, q->bits_per_frame, dec*10, (float)q->bits_per_frame/(dec*0.01)); + fprintf(stderr, "\n"); + + char frame[q->bits_per_frame]; + int bits_read = 0; + + LPCNetState *net = lpcnet_create(); + + fin = stdin; + fout = stdout; + + do { + float in_features[NB_TOTAL_FEATURES]; + float features[NB_TOTAL_FEATURES]; + short pcm[FRAME_SIZE]; + if ((q->f % q->dec) == 0) + bits_read = fread(frame, sizeof(char), q->bits_per_frame, fin); + lpcnet_frame_to_features(q, frame, in_features); + + RNN_COPY(features, in_features, NB_TOTAL_FEATURES); + RNN_CLEAR(&features[18], 18); + lpcnet_synthesize(net, pcm, features, FRAME_SIZE); + fwrite(pcm, sizeof(pcm[0]), FRAME_SIZE, fout); + if (fout == stdout) fflush(stdout); + } while(bits_read != 0); + + fclose(fin); + fclose(fout); + lpcnet_destroy(net); lpcnet_quant_destroy(q); + return 0; +} diff --git a/src/lpcnet_enc.c b/src/lpcnet_enc.c index 167b41f..d97f1b6 100644 --- a/src/lpcnet_enc.c +++ b/src/lpcnet_enc.c @@ -2,9 +2,9 @@ lpcnet_enc.c Feb 2019 - LPCNet to bit stream encoder, takes 16 kHz signed 16 samples on - stdin, outputs fully quantised bit stream on stdout (in 1 bit per - char format). + LPCNet to bit stream encoder, takes 16 kHz signed 16 bit speech + samples on stdin, outputs fully quantised bit stream on stdout (in + 1 bit per char format). */ /* Copyright (c) 2017-2018 Mozilla */ diff --git a/src/test_lpcnet.c b/src/test_lpcnet.c index 9da9c77..8841de4 100644 --- a/src/test_lpcnet.c +++ b/src/test_lpcnet.c @@ -58,7 +58,7 @@ int main(int argc, char **argv) { } } - while (1) { + while (1) { float in_features[NB_TOTAL_FEATURES]; float features[NB_FEATURES]; short pcm[FRAME_SIZE];