From 65458ee71ca741fbed1bff7e358760bb480b85e9 Mon Sep 17 00:00:00 2001 From: Thom Johansen Date: Fri, 16 Nov 2007 15:35:37 +0000 Subject: Speex encoder specially tailored to create voice UI snippets. Small fixups to libspeex to allow it to be built. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15640 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/libspeex/Makefile | 2 +- apps/codecs/libspeex/SOURCES | 10 ++ apps/codecs/libspeex/bits.c | 2 +- apps/codecs/libspeex/config-speex.h | 14 +++ apps/codecs/libspeex/rockbox.h | 5 + apps/codecs/libspeex/speex_header.c | 2 +- tools/Makefile | 6 +- tools/rbspeex/Makefile | 56 +++++++++ tools/rbspeex/rbspeexenc.c | 239 ++++++++++++++++++++++++++++++++++++ 9 files changed, 332 insertions(+), 4 deletions(-) create mode 100644 tools/rbspeex/Makefile create mode 100644 tools/rbspeex/rbspeexenc.c diff --git a/apps/codecs/libspeex/Makefile b/apps/codecs/libspeex/Makefile index 35a8535369..3bda53dca7 100644 --- a/apps/codecs/libspeex/Makefile +++ b/apps/codecs/libspeex/Makefile @@ -14,7 +14,7 @@ ifdef APPEXTRA INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) endif -SPEEXOPTS = -DHAVE_CONFIG_H -DSPEEX_DISABLE_ENCODER +SPEEXOPTS = -DHAVE_CONFIG_H -DSPEEX_DISABLE_ENCODER -DROCKBOX # We're faster on ARM-targets with -O1 instead of -O2 ifeq ($(CPU),arm) diff --git a/apps/codecs/libspeex/SOURCES b/apps/codecs/libspeex/SOURCES index 40ea5380b9..f036734589 100644 --- a/apps/codecs/libspeex/SOURCES +++ b/apps/codecs/libspeex/SOURCES @@ -24,12 +24,22 @@ speex.c speex_callbacks.c speex_header.c #ifndef ROCKBOX_VOICE_CODEC +#ifndef ROCKBOX_VOICE_ENCODER oggframing.c +#endif stereo.c #endif +#ifdef ROCKBOX_VOICE_ENCODER +lpc.c +vbr.c +vq.c +window.c +resample.c +#else #ifdef CPU_COLDFIRE filters_cf.S ltp_cf.S #elif defined(CPU_ARM) filters_arm4.S #endif +#endif diff --git a/apps/codecs/libspeex/bits.c b/apps/codecs/libspeex/bits.c index d4b02a5a86..e460a39cf2 100644 --- a/apps/codecs/libspeex/bits.c +++ b/apps/codecs/libspeex/bits.c @@ -106,7 +106,7 @@ void speex_bits_rewind(SpeexBits *bits) bits->overflow=0; } -#if 0 +#ifndef SPEEX_VOICE_ENCODER void speex_bits_read_from(SpeexBits *bits, char *chars, int len) { int i; diff --git a/apps/codecs/libspeex/config-speex.h b/apps/codecs/libspeex/config-speex.h index 1378fc2a69..ad1393fc60 100644 --- a/apps/codecs/libspeex/config-speex.h +++ b/apps/codecs/libspeex/config-speex.h @@ -1,8 +1,16 @@ +#ifndef ROCKBOX_VOICE_ENCODER #include "../codec.h" #include "autoconf.h" +#else +#define ICODE_ATTR +#define IDATA_ATTR +#define IBSS_ATTR +#define ICONST_ATTR +#endif /* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ +#ifndef ROCKBOX_VOICE_ENCODER /* Make use of ARM4E assembly optimizations */ #if defined(CPU_ARM) #define ARM4_ASM @@ -15,6 +23,7 @@ /* Make use of Blackfin assembly optimizations */ /* #undef BFIN_ASM */ +#endif /* ROCKBOX_VOICE_ENCODER */ /* Disable wideband codec */ /* #undef DISABLE_WIDEBAND */ @@ -28,8 +37,13 @@ /* Debug fixed-point implementation */ /* #undef FIXED_DEBUG */ +#ifndef ROCKBOX_VOICE_ENCODER /* Compile target codec as fixed point */ #define FIXED_POINT +#else +/* Compile voice clip encoder as floating point */ +#define FLOATING_POINT +#endif /* Define to 1 if you have the header file. */ /* #undef HAVE_DLFCN_H */ diff --git a/apps/codecs/libspeex/rockbox.h b/apps/codecs/libspeex/rockbox.h index 0f8c6d932f..0e0d3ed647 100644 --- a/apps/codecs/libspeex/rockbox.h +++ b/apps/codecs/libspeex/rockbox.h @@ -19,6 +19,9 @@ #ifndef SPEEX_ROCKBOX_H #define SPEEX_ROCKBOX_H +/* We don't want all this stuff if we're building encoder */ +#ifndef ROCKBOX_VOICE_ENCODER + #include "../codec.h" #include "../lib/codeclib.h" @@ -106,5 +109,7 @@ static inline void _speex_putc(int ch, void *file) //printf("%c", ch); } +#endif /* ROCKBOX_VOICE_ENCODER */ + #endif diff --git a/apps/codecs/libspeex/speex_header.c b/apps/codecs/libspeex/speex_header.c index eb4c199d46..30f47206c4 100644 --- a/apps/codecs/libspeex/speex_header.c +++ b/apps/codecs/libspeex/speex_header.c @@ -47,7 +47,7 @@ /** Convert little endian */ static inline spx_int32_t le_int(spx_int32_t i) { -#if 1 +#ifdef ROCKBOX return letoh32(i); #elif !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) ) spx_uint32_t ui, ret; diff --git a/tools/Makefile b/tools/Makefile index 4b72b4c638..50d05abda8 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -11,7 +11,7 @@ LDFLAGS := -g CLEANALL := scramble descramble iriver sh2d bmp2rb rdf2binary convbdf \ generate_rocklatin mkboot ipod_fw codepages uclpack mi4 gigabeat database \ - lngdump telechips gigabeats mktccboot mknkboot + lngdump telechips gigabeats mktccboot mknkboot rbspeexenc all: @echo "Run make in your build directory!" @@ -76,6 +76,9 @@ player_unifont: player_unifont.c ../firmware/drivers/lcd-charset-player.c uclpack: $(SILENT)$(MAKE) -C ucl +rbspeexenc: + $(SILENT)$(MAKE) -C rbspeex + wavtrim: wavtrim.c $(SILENT)$(CC) -g $+ -o $@ @@ -86,4 +89,5 @@ clean: @echo "Cleaning tools" $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~ $(SILENT)$(MAKE) -C ucl clean + $(SILENT)$(MAKE) -C rbspeex clean diff --git a/tools/rbspeex/Makefile b/tools/rbspeex/Makefile new file mode 100644 index 0000000000..4771fe4ea2 --- /dev/null +++ b/tools/rbspeex/Makefile @@ -0,0 +1,56 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id: Makefile,v 1.16 2006-09-02 22:34:13 bagder Exp $ +# + +ifndef V +SILENT = @ +endif + +SPEEXSRC = ../../apps/codecs/libspeex + +INCLUDES = -I $(SPEEXSRC) -iquote $(SPEEXSRC) +SPEEXOPTS = -DHAVE_CONFIG_H -DROCKBOX_VOICE_ENCODER + +CFLAGS = $(SPEEXOPTS) $(INCLUDES) -O3 -fomit-frame-pointer -Wno-unused-parameter + +# This sets up 'SRC' based on the files mentioned in SOURCES +SRC := $(shell cat $(SPEEXSRC)/SOURCES | $(CC) $(CFLAGS) -E -P - | grep -v "^\#") + +SOURCES = $(SRC:%.c=$(SPEEXSRC)/%.c) rbspeexenc.c +OBJS := $(SRC:%.c=%.o) rbspeexenc.o +DEPFILE = dep-speex +DIRS = + +.PHONY : all + +all: ../rbspeexenc + +$(DEPFILE): $(SOURCES) + $(SILENT)rm -f $(DEPFILE) + $(SILENT)(for each in $(SOURCES) x; do \ + if test "x" != "$$each"; then \ + obj=`echo $$each | sed -e 's/\.[cS]/.o/' | sed -e 's/^.*\///' `; \ + $(CC) -MG -MM -MT "$$obj" $(CFLAGS) $$each 2>/dev/null; \ + fi; \ + if test -n "$$del"; then \ + rm $$del; \ + del=""; \ + fi \ + done > $(DEPFILE); \ + echo "oo" > /dev/null ) + +../rbspeexenc: $(OBJS) $(DEPFILE) + gcc -o ../rbspeexenc $(OBJS) -lm + +%.o: + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJS) dep-speex + +-include $(DEPFILE) diff --git a/tools/rbspeex/rbspeexenc.c b/tools/rbspeex/rbspeexenc.c new file mode 100644 index 0000000000..5d301998da --- /dev/null +++ b/tools/rbspeex/rbspeexenc.c @@ -0,0 +1,239 @@ +/************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2007 Thom Johansen + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/* Read an unaligned 32-bit little endian long from buffer. */ +unsigned int get_long_le(unsigned char *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +bool get_wave_metadata(FILE *fd, int *numchan, int *bps, int *sr, int *numsamples) +{ + unsigned char buf[1024]; + unsigned long totalsamples = 0; + unsigned long channels = 0; + unsigned long bitspersample = 0; + unsigned long numbytes = 0; + size_t read_bytes; + int i; + + if ((read_bytes = fread(buf, 1, 12, fd)) < 12) + return false; + + if ((memcmp(buf, "RIFF",4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) + return false; + + /* iterate over WAVE chunks until 'data' chunk */ + while (1) { + /* get chunk header */ + if ((read_bytes = fread(buf, 1, 8, fd)) < 8) + return false; + + /* chunkSize */ + i = get_long_le(&buf[4]); + + if (memcmp(buf, "fmt ", 4) == 0) { + /* get rest of chunk */ + if ((read_bytes = fread(buf, 1, 16, fd)) < 16) + return false; + + i -= 16; + + channels = *numchan = buf[2] | (buf[3] << 8); + *sr = get_long_le(&buf[4]); + /* wBitsPerSample */ + bitspersample = *bps = buf[14] | (buf[15] << 8); + } else if (memcmp(buf, "data", 4) == 0) { + numbytes = i; + break; + } else if (memcmp(buf, "fact", 4) == 0) { + /* dwSampleLength */ + if (i >= 4) { + /* get rest of chunk */ + if ((read_bytes = read(buf, 1, 4, fd)) < 4) + return false; + + i -= 4; + totalsamples = get_long_le(buf); + } + } + + /* seek to next chunk (even chunk sizes must be padded) */ + if (i & 0x01) + i++; + + if (fseek(fd, i, SEEK_CUR) < 0) + return false; + } + + if ((numbytes == 0) || (channels == 0)) + return false; + + if (totalsamples == 0) { + /* for PCM only */ + totalsamples = numbytes/((((bitspersample - 1) / 8) + 1)*channels); + } + *numsamples = totalsamples; + return true; +} + +int main(int argc, char **argv) +{ + FILE *fin, *fout; + spx_int16_t *in, *inpos; + spx_int16_t enc_buf[640]; /* Max frame size */ + char cbits[200]; + int nbytes; + void *st; + SpeexResamplerState *resampler = NULL; + SpeexBits bits; + int i, tmp; + float ftmp; + int numchan, bps, sr, numsamples; + int frame_size; + + if (argc < 3) { + printf("Usage: rbspeexenc [options] infile outfile\n" + "Options:\n" + " -q x Quality, floating point number in the range [0-10]\n" + " -c x Complexity, affects quality and encoding time, where\n" + " both increase with increasing values, range [0-10]\n" + " Defaults are as in speexenc.\n" + "\nWARNING: This tool will create files that are only usable by Rockbox!\n" + ); + return 1; + } + + /* We'll eat an entire WAV file here, and encode it with Speex, packing the + * bits as tightly as we can. Output is completely raw, with absolutely + * nothing to identify the contents. + */ + + /* Wideband encoding */ + st = speex_encoder_init(&speex_wb_mode); + + /* VBR */ + tmp = 1; + speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); + /* Quality, 0-10 */ + ftmp = 8.f; + for (i = 1; i < argc - 2; ++i) { + if (strncmp(argv[i], "-q", 2) == 0) { + ftmp = atof(argv[i + 1]); + break; + } + } + speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &ftmp); + /* Complexity, 0-10 */ + tmp = 3; + for (i = 1; i < argc - 2; ++i) { + if (strncmp(argv[i], "-c", 2) == 0) { + tmp = atoi(argv[i + 1]); + break; + } + } + speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp); + speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size); + + fin = fopen(argv[argc - 2], "rb"); + if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) { + printf("invalid wave file!\n"); + return 1; + } + if (sr != 16000) { + resampler = speex_resampler_init(1, sr, 16000, 10, NULL); + speex_resampler_skip_zeros(resampler); + printf("Resampling from %i Hz to 16000 Hz\n", sr); + } + if (numchan != 1) { + printf("Error: input file must be mono\n"); + return 1; + } + if (bps != 16) { + printf("samples must be 16 bit!\n"); + return 1; + } + + /* Read input samples into a buffer */ + in = malloc(numsamples*2); + if (malloc == NULL) { + printf("error on malloc\n"); + return 1; + } + fread(in, 2, numsamples, fin); + fclose(fin); + + speex_bits_init(&bits); + inpos = in; + fout = fopen(argv[argc - 1], "wb"); + + while (numsamples > 0) { + int samples = frame_size; + + /* Check if we need to resample */ + if (sr != 16000) { + spx_uint32_t in_len = numsamples, out_len = frame_size; + + /* Limit this or resampler will try to allocate it all on stack */ + if (in_len > 2000) + in_len = 2000; + speex_resampler_process_int(resampler, 0, inpos, &in_len, + enc_buf, &out_len); + inpos += in_len; + samples = out_len; + numsamples -= in_len; + } else { + if (samples > numsamples) + samples = numsamples; + memcpy(enc_buf, inpos, samples*2); + inpos += frame_size; + numsamples -= frame_size; + } + /* Pad out with zeros if we didn't fill all input */ + memset(enc_buf + samples, 0, (frame_size - samples)*2); + + speex_encode_int(st, enc_buf, &bits); + + /* Copy the bits to an array of char that can be written */ + nbytes = speex_bits_write_whole_bytes(&bits, cbits, 200); + + /* Write the compressed data */ + fwrite(cbits, 1, nbytes, fout); + } + + /* Squeeze out the last bits */ + nbytes = speex_bits_write(&bits, cbits, 200); + fwrite(cbits, 1, nbytes, fout); + + /*Destroy the encoder state*/ + speex_encoder_destroy(st); + /*Destroy the bit-packing struct*/ + speex_bits_destroy(&bits); + if (resampler != NULL) + speex_resampler_destroy(resampler); + fclose(fout); + free(in); + return 0; +} -- cgit v1.2.3