diff options
author | Thom Johansen <thomj@rockbox.org> | 2007-11-16 15:35:37 +0000 |
---|---|---|
committer | Thom Johansen <thomj@rockbox.org> | 2007-11-16 15:35:37 +0000 |
commit | 65458ee71ca741fbed1bff7e358760bb480b85e9 (patch) | |
tree | be9262a5c2560e5e0710d41e6c94d0c3a4e86b77 /tools | |
parent | 88c4748a4b93b6dcf0a8288c030bf93ae571ce5e (diff) | |
download | rockbox-65458ee71ca741fbed1bff7e358760bb480b85e9.tar.gz rockbox-65458ee71ca741fbed1bff7e358760bb480b85e9.zip |
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
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile | 6 | ||||
-rw-r--r-- | tools/rbspeex/Makefile | 56 | ||||
-rw-r--r-- | tools/rbspeex/rbspeexenc.c | 239 |
3 files changed, 300 insertions, 1 deletions
diff --git a/tools/Makefile b/tools/Makefile index 4b72b4c638..50d05abda8 100644 --- a/tools/Makefile +++ b/tools/Makefile | |||
@@ -11,7 +11,7 @@ LDFLAGS := -g | |||
11 | 11 | ||
12 | CLEANALL := scramble descramble iriver sh2d bmp2rb rdf2binary convbdf \ | 12 | CLEANALL := scramble descramble iriver sh2d bmp2rb rdf2binary convbdf \ |
13 | generate_rocklatin mkboot ipod_fw codepages uclpack mi4 gigabeat database \ | 13 | generate_rocklatin mkboot ipod_fw codepages uclpack mi4 gigabeat database \ |
14 | lngdump telechips gigabeats mktccboot mknkboot | 14 | lngdump telechips gigabeats mktccboot mknkboot rbspeexenc |
15 | 15 | ||
16 | all: | 16 | all: |
17 | @echo "Run make in your build directory!" | 17 | @echo "Run make in your build directory!" |
@@ -76,6 +76,9 @@ player_unifont: player_unifont.c ../firmware/drivers/lcd-charset-player.c | |||
76 | uclpack: | 76 | uclpack: |
77 | $(SILENT)$(MAKE) -C ucl | 77 | $(SILENT)$(MAKE) -C ucl |
78 | 78 | ||
79 | rbspeexenc: | ||
80 | $(SILENT)$(MAKE) -C rbspeex | ||
81 | |||
79 | wavtrim: wavtrim.c | 82 | wavtrim: wavtrim.c |
80 | $(SILENT)$(CC) -g $+ -o $@ | 83 | $(SILENT)$(CC) -g $+ -o $@ |
81 | 84 | ||
@@ -86,4 +89,5 @@ clean: | |||
86 | @echo "Cleaning tools" | 89 | @echo "Cleaning tools" |
87 | $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~ | 90 | $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do echo $$f.exe $$f.o $$f.obj ; done) *.ajf *~ |
88 | $(SILENT)$(MAKE) -C ucl clean | 91 | $(SILENT)$(MAKE) -C ucl clean |
92 | $(SILENT)$(MAKE) -C rbspeex clean | ||
89 | 93 | ||
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 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id: Makefile,v 1.16 2006-09-02 22:34:13 bagder Exp $ | ||
8 | # | ||
9 | |||
10 | ifndef V | ||
11 | SILENT = @ | ||
12 | endif | ||
13 | |||
14 | SPEEXSRC = ../../apps/codecs/libspeex | ||
15 | |||
16 | INCLUDES = -I $(SPEEXSRC) -iquote $(SPEEXSRC) | ||
17 | SPEEXOPTS = -DHAVE_CONFIG_H -DROCKBOX_VOICE_ENCODER | ||
18 | |||
19 | CFLAGS = $(SPEEXOPTS) $(INCLUDES) -O3 -fomit-frame-pointer -Wno-unused-parameter | ||
20 | |||
21 | # This sets up 'SRC' based on the files mentioned in SOURCES | ||
22 | SRC := $(shell cat $(SPEEXSRC)/SOURCES | $(CC) $(CFLAGS) -E -P - | grep -v "^\#") | ||
23 | |||
24 | SOURCES = $(SRC:%.c=$(SPEEXSRC)/%.c) rbspeexenc.c | ||
25 | OBJS := $(SRC:%.c=%.o) rbspeexenc.o | ||
26 | DEPFILE = dep-speex | ||
27 | DIRS = | ||
28 | |||
29 | .PHONY : all | ||
30 | |||
31 | all: ../rbspeexenc | ||
32 | |||
33 | $(DEPFILE): $(SOURCES) | ||
34 | $(SILENT)rm -f $(DEPFILE) | ||
35 | $(SILENT)(for each in $(SOURCES) x; do \ | ||
36 | if test "x" != "$$each"; then \ | ||
37 | obj=`echo $$each | sed -e 's/\.[cS]/.o/' | sed -e 's/^.*\///' `; \ | ||
38 | $(CC) -MG -MM -MT "$$obj" $(CFLAGS) $$each 2>/dev/null; \ | ||
39 | fi; \ | ||
40 | if test -n "$$del"; then \ | ||
41 | rm $$del; \ | ||
42 | del=""; \ | ||
43 | fi \ | ||
44 | done > $(DEPFILE); \ | ||
45 | echo "oo" > /dev/null ) | ||
46 | |||
47 | ../rbspeexenc: $(OBJS) $(DEPFILE) | ||
48 | gcc -o ../rbspeexenc $(OBJS) -lm | ||
49 | |||
50 | %.o: | ||
51 | $(CC) $(CFLAGS) -c $< -o $@ | ||
52 | |||
53 | clean: | ||
54 | rm -f $(OBJS) dep-speex | ||
55 | |||
56 | -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 @@ | |||
1 | /************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2007 Thom Johansen | ||
10 | * | ||
11 | * All files in this archive are subject to the GNU General Public License. | ||
12 | * See the file COPYING in the source tree root for full license agreement. | ||
13 | * | ||
14 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
15 | * KIND, either express or implied. | ||
16 | * | ||
17 | ***************************************************************************/ | ||
18 | |||
19 | #include <speex/speex.h> | ||
20 | #include <speex/speex_resampler.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <stdbool.h> | ||
25 | |||
26 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
27 | unsigned int get_long_le(unsigned char *p) | ||
28 | { | ||
29 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
30 | } | ||
31 | |||
32 | bool get_wave_metadata(FILE *fd, int *numchan, int *bps, int *sr, int *numsamples) | ||
33 | { | ||
34 | unsigned char buf[1024]; | ||
35 | unsigned long totalsamples = 0; | ||
36 | unsigned long channels = 0; | ||
37 | unsigned long bitspersample = 0; | ||
38 | unsigned long numbytes = 0; | ||
39 | size_t read_bytes; | ||
40 | int i; | ||
41 | |||
42 | if ((read_bytes = fread(buf, 1, 12, fd)) < 12) | ||
43 | return false; | ||
44 | |||
45 | if ((memcmp(buf, "RIFF",4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) | ||
46 | return false; | ||
47 | |||
48 | /* iterate over WAVE chunks until 'data' chunk */ | ||
49 | while (1) { | ||
50 | /* get chunk header */ | ||
51 | if ((read_bytes = fread(buf, 1, 8, fd)) < 8) | ||
52 | return false; | ||
53 | |||
54 | /* chunkSize */ | ||
55 | i = get_long_le(&buf[4]); | ||
56 | |||
57 | if (memcmp(buf, "fmt ", 4) == 0) { | ||
58 | /* get rest of chunk */ | ||
59 | if ((read_bytes = fread(buf, 1, 16, fd)) < 16) | ||
60 | return false; | ||
61 | |||
62 | i -= 16; | ||
63 | |||
64 | channels = *numchan = buf[2] | (buf[3] << 8); | ||
65 | *sr = get_long_le(&buf[4]); | ||
66 | /* wBitsPerSample */ | ||
67 | bitspersample = *bps = buf[14] | (buf[15] << 8); | ||
68 | } else if (memcmp(buf, "data", 4) == 0) { | ||
69 | numbytes = i; | ||
70 | break; | ||
71 | } else if (memcmp(buf, "fact", 4) == 0) { | ||
72 | /* dwSampleLength */ | ||
73 | if (i >= 4) { | ||
74 | /* get rest of chunk */ | ||
75 | if ((read_bytes = read(buf, 1, 4, fd)) < 4) | ||
76 | return false; | ||
77 | |||
78 | i -= 4; | ||
79 | totalsamples = get_long_le(buf); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /* seek to next chunk (even chunk sizes must be padded) */ | ||
84 | if (i & 0x01) | ||
85 | i++; | ||
86 | |||
87 | if (fseek(fd, i, SEEK_CUR) < 0) | ||
88 | return false; | ||
89 | } | ||
90 | |||
91 | if ((numbytes == 0) || (channels == 0)) | ||
92 | return false; | ||
93 | |||
94 | if (totalsamples == 0) { | ||
95 | /* for PCM only */ | ||
96 | totalsamples = numbytes/((((bitspersample - 1) / 8) + 1)*channels); | ||
97 | } | ||
98 | *numsamples = totalsamples; | ||
99 | return true; | ||
100 | } | ||
101 | |||
102 | int main(int argc, char **argv) | ||
103 | { | ||
104 | FILE *fin, *fout; | ||
105 | spx_int16_t *in, *inpos; | ||
106 | spx_int16_t enc_buf[640]; /* Max frame size */ | ||
107 | char cbits[200]; | ||
108 | int nbytes; | ||
109 | void *st; | ||
110 | SpeexResamplerState *resampler = NULL; | ||
111 | SpeexBits bits; | ||
112 | int i, tmp; | ||
113 | float ftmp; | ||
114 | int numchan, bps, sr, numsamples; | ||
115 | int frame_size; | ||
116 | |||
117 | if (argc < 3) { | ||
118 | printf("Usage: rbspeexenc [options] infile outfile\n" | ||
119 | "Options:\n" | ||
120 | " -q x Quality, floating point number in the range [0-10]\n" | ||
121 | " -c x Complexity, affects quality and encoding time, where\n" | ||
122 | " both increase with increasing values, range [0-10]\n" | ||
123 | " Defaults are as in speexenc.\n" | ||
124 | "\nWARNING: This tool will create files that are only usable by Rockbox!\n" | ||
125 | ); | ||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | /* We'll eat an entire WAV file here, and encode it with Speex, packing the | ||
130 | * bits as tightly as we can. Output is completely raw, with absolutely | ||
131 | * nothing to identify the contents. | ||
132 | */ | ||
133 | |||
134 | /* Wideband encoding */ | ||
135 | st = speex_encoder_init(&speex_wb_mode); | ||
136 | |||
137 | /* VBR */ | ||
138 | tmp = 1; | ||
139 | speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); | ||
140 | /* Quality, 0-10 */ | ||
141 | ftmp = 8.f; | ||
142 | for (i = 1; i < argc - 2; ++i) { | ||
143 | if (strncmp(argv[i], "-q", 2) == 0) { | ||
144 | ftmp = atof(argv[i + 1]); | ||
145 | break; | ||
146 | } | ||
147 | } | ||
148 | speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &ftmp); | ||
149 | /* Complexity, 0-10 */ | ||
150 | tmp = 3; | ||
151 | for (i = 1; i < argc - 2; ++i) { | ||
152 | if (strncmp(argv[i], "-c", 2) == 0) { | ||
153 | tmp = atoi(argv[i + 1]); | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp); | ||
158 | speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size); | ||
159 | |||
160 | fin = fopen(argv[argc - 2], "rb"); | ||
161 | if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) { | ||
162 | printf("invalid wave file!\n"); | ||
163 | return 1; | ||
164 | } | ||
165 | if (sr != 16000) { | ||
166 | resampler = speex_resampler_init(1, sr, 16000, 10, NULL); | ||
167 | speex_resampler_skip_zeros(resampler); | ||
168 | printf("Resampling from %i Hz to 16000 Hz\n", sr); | ||
169 | } | ||
170 | if (numchan != 1) { | ||
171 | printf("Error: input file must be mono\n"); | ||
172 | return 1; | ||
173 | } | ||
174 | if (bps != 16) { | ||
175 | printf("samples must be 16 bit!\n"); | ||
176 | return 1; | ||
177 | } | ||
178 | |||
179 | /* Read input samples into a buffer */ | ||
180 | in = malloc(numsamples*2); | ||
181 | if (malloc == NULL) { | ||
182 | printf("error on malloc\n"); | ||
183 | return 1; | ||
184 | } | ||
185 | fread(in, 2, numsamples, fin); | ||
186 | fclose(fin); | ||
187 | |||
188 | speex_bits_init(&bits); | ||
189 | inpos = in; | ||
190 | fout = fopen(argv[argc - 1], "wb"); | ||
191 | |||
192 | while (numsamples > 0) { | ||
193 | int samples = frame_size; | ||
194 | |||
195 | /* Check if we need to resample */ | ||
196 | if (sr != 16000) { | ||
197 | spx_uint32_t in_len = numsamples, out_len = frame_size; | ||
198 | |||
199 | /* Limit this or resampler will try to allocate it all on stack */ | ||
200 | if (in_len > 2000) | ||
201 | in_len = 2000; | ||
202 | speex_resampler_process_int(resampler, 0, inpos, &in_len, | ||
203 | enc_buf, &out_len); | ||
204 | inpos += in_len; | ||
205 | samples = out_len; | ||
206 | numsamples -= in_len; | ||
207 | } else { | ||
208 | if (samples > numsamples) | ||
209 | samples = numsamples; | ||
210 | memcpy(enc_buf, inpos, samples*2); | ||
211 | inpos += frame_size; | ||
212 | numsamples -= frame_size; | ||
213 | } | ||
214 | /* Pad out with zeros if we didn't fill all input */ | ||
215 | memset(enc_buf + samples, 0, (frame_size - samples)*2); | ||
216 | |||
217 | speex_encode_int(st, enc_buf, &bits); | ||
218 | |||
219 | /* Copy the bits to an array of char that can be written */ | ||
220 | nbytes = speex_bits_write_whole_bytes(&bits, cbits, 200); | ||
221 | |||
222 | /* Write the compressed data */ | ||
223 | fwrite(cbits, 1, nbytes, fout); | ||
224 | } | ||
225 | |||
226 | /* Squeeze out the last bits */ | ||
227 | nbytes = speex_bits_write(&bits, cbits, 200); | ||
228 | fwrite(cbits, 1, nbytes, fout); | ||
229 | |||
230 | /*Destroy the encoder state*/ | ||
231 | speex_encoder_destroy(st); | ||
232 | /*Destroy the bit-packing struct*/ | ||
233 | speex_bits_destroy(&bits); | ||
234 | if (resampler != NULL) | ||
235 | speex_resampler_destroy(resampler); | ||
236 | fclose(fout); | ||
237 | free(in); | ||
238 | return 0; | ||
239 | } | ||