forked from len0rd/rockbox
refactor rbspeex, so we build a librbspeex.a for linking into rbutil.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15924 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
b2f7c61f84
commit
ed047d9db1
5 changed files with 309 additions and 240 deletions
|
@ -26,7 +26,7 @@ endif
|
||||||
# This sets up 'SRC' based on the files mentioned in SOURCES
|
# This sets up 'SRC' based on the files mentioned in SOURCES
|
||||||
SRC := $(shell cat $(SPEEXSRC)/SOURCES | $(CC) $(CFLAGS) -E -P - | grep -v "^\#")
|
SRC := $(shell cat $(SPEEXSRC)/SOURCES | $(CC) $(CFLAGS) -E -P - | grep -v "^\#")
|
||||||
|
|
||||||
SOURCES = $(SRC:%.c=$(SPEEXSRC)/%.c) rbspeexenc.c rbspeexdec.c
|
SOURCES = $(SRC:%.c=$(SPEEXSRC)/%.c) rbspeex.c rbspeexenc.c rbspeexdec.c
|
||||||
OBJS := $(SRC:%.c=%.o)
|
OBJS := $(SRC:%.c=%.o)
|
||||||
DEPFILE = dep-speex
|
DEPFILE = dep-speex
|
||||||
DIRS =
|
DIRS =
|
||||||
|
@ -49,23 +49,24 @@ $(DEPFILE): $(SOURCES)
|
||||||
done > $(DEPFILE); \
|
done > $(DEPFILE); \
|
||||||
echo "oo" > /dev/null )
|
echo "oo" > /dev/null )
|
||||||
|
|
||||||
libspeex.a: $(OBJS) $(DEPFILE)
|
librbspeex.a: $(OBJS) $(DEPFILE) rbspeex.o
|
||||||
@echo AR libspeex.a
|
@echo AR librbspeex.a
|
||||||
$(SILENT)$(AR) ruv $@ $+ > /dev/null 2>&1
|
$(AR) ruv $@ $+ > /dev/null 2>&1
|
||||||
|
|
||||||
../rbspeexenc: $(OBJS) libspeex.a rbspeexenc.o
|
../rbspeexenc: $(OBJS) rbspeexenc.o librbspeex.a
|
||||||
@echo Linking ../rbspeexenc
|
@echo Linking ../rbspeexenc
|
||||||
$(SILENT)$(CC) $(CFLAGS) -o ../rbspeexenc rbspeexenc.o libspeex.a -lm
|
$(SILENT)$(CC) $(CFLAGS) -o ../rbspeexenc rbspeexenc.o librbspeex.a -lm
|
||||||
|
|
||||||
../rbspeexdec: $(OBJS) libspeex.a rbspeexdec.o
|
../rbspeexdec: $(OBJS) librbspeex.a rbspeexdec.o
|
||||||
@echo Linking ../rbspeexdec
|
@echo Linking ../rbspeexdec
|
||||||
$(SILENT)$(CC) $(CFLAGS) -o ../rbspeexdec rbspeexdec.o libspeex.a -lm
|
$(SILENT)$(CC) $(CFLAGS) -o ../rbspeexdec rbspeexdec.o librbspeex.a -lm
|
||||||
|
|
||||||
%.o:
|
%.o:
|
||||||
@echo CC $<
|
@echo CC $<
|
||||||
$(SILENT)$(CC) $(CFLAGS) -c $< -o $@
|
$(SILENT)$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJS) libspeex.a rbspeexenc.o ../rbspeexenc dep-speex
|
rm -f $(OBJS) libspeex.a librbspeex.a rbspeexenc.o ../rbspeexenc dep-speex
|
||||||
|
|
||||||
-include $(DEPFILE)
|
-include $(DEPFILE)
|
||||||
|
|
261
tools/rbspeex/rbspeex.c
Normal file
261
tools/rbspeex/rbspeex.c
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
/**************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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 <speex/speex.h>
|
||||||
|
#include <speex/speex_resampler.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "rbspeex.h"
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void put_ushort_le(unsigned short x, unsigned char *out)
|
||||||
|
{
|
||||||
|
out[0] = x & 0xff;
|
||||||
|
out[1] = x >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void put_uint_le(unsigned int x, unsigned char *out)
|
||||||
|
{
|
||||||
|
out[0] = x & 0xff;
|
||||||
|
out[1] = (x >> 8) & 0xff;
|
||||||
|
out[2] = (x >> 16) & 0xff;
|
||||||
|
out[3] = x >> 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 = fread(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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. Files are left open, so remember to close
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
bool encode_file(FILE *fin, FILE *fout, float quality, int complexity,
|
||||||
|
bool narrowband, float volume, char *errstr, size_t errlen)
|
||||||
|
{
|
||||||
|
spx_int16_t *in = NULL, *inpos;
|
||||||
|
spx_int16_t enc_buf[640]; /* Max frame size */
|
||||||
|
char cbits[200];
|
||||||
|
void *st = NULL;
|
||||||
|
SpeexResamplerState *resampler = NULL;
|
||||||
|
SpeexBits bits;
|
||||||
|
int i, tmp, target_sr, numchan, bps, sr, numsamples, frame_size, lookahead;
|
||||||
|
int nbytes;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) {
|
||||||
|
snprintf(errstr, errlen, "invalid WAV file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (numchan != 1) {
|
||||||
|
snprintf(errstr, errlen, "input file must be mono");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bps != 16) {
|
||||||
|
snprintf(errstr, errlen, "samples must be 16 bit");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate an encoder of specified type, defaults to wideband */
|
||||||
|
st = speex_encoder_init(narrowband ? &speex_nb_mode : &speex_wb_mode);
|
||||||
|
if (narrowband)
|
||||||
|
target_sr = 8000;
|
||||||
|
else
|
||||||
|
target_sr = 16000;
|
||||||
|
speex_bits_init(&bits);
|
||||||
|
|
||||||
|
/* VBR */
|
||||||
|
tmp = 1;
|
||||||
|
speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
|
||||||
|
/* Quality, 0-10 */
|
||||||
|
speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &quality);
|
||||||
|
/* Complexity, 0-10 */
|
||||||
|
speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
|
||||||
|
speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
|
||||||
|
speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
|
||||||
|
|
||||||
|
/* Read input samples into a buffer */
|
||||||
|
in = calloc(numsamples + lookahead, sizeof(spx_int16_t));
|
||||||
|
if (in == NULL) {
|
||||||
|
snprintf(errstr, errlen, "could not allocate clip memory");
|
||||||
|
ret = false;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (fread(in, 2, numsamples, fin) != numsamples) {
|
||||||
|
snprintf(errstr, errlen, "could not read input file data");
|
||||||
|
ret = false;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume != 1.0f) {
|
||||||
|
for (i = 0; i < numsamples; ++i)
|
||||||
|
in[i] *= volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sr != target_sr) {
|
||||||
|
resampler = speex_resampler_init(1, sr, target_sr, 10, NULL);
|
||||||
|
speex_resampler_skip_zeros(resampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There will be 'lookahead' samples of zero at the end of the array, to
|
||||||
|
* make sure the Speex encoder is allowed to spit out all its data at clip
|
||||||
|
* end */
|
||||||
|
numsamples += lookahead;
|
||||||
|
|
||||||
|
inpos = in;
|
||||||
|
while (numsamples > 0) {
|
||||||
|
int samples = frame_size;
|
||||||
|
|
||||||
|
/* Check if we need to resample */
|
||||||
|
if (sr != target_sr) {
|
||||||
|
spx_uint32_t in_len = numsamples, out_len = frame_size;
|
||||||
|
double resample_factor = (double)sr/(double)target_sr;
|
||||||
|
/* Calculate how many input samples are needed for one full frame
|
||||||
|
* out, and add some, just in case. */
|
||||||
|
spx_uint32_t samples_in = frame_size*resample_factor + 50;
|
||||||
|
|
||||||
|
/* Limit this or resampler will try to allocate it all on stack */
|
||||||
|
if (in_len > samples_in)
|
||||||
|
in_len = samples_in;
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (speex_encode_int(st, enc_buf, &bits) < 0) {
|
||||||
|
snprintf(errstr, errlen, "encoder error");
|
||||||
|
ret = false;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
if (fwrite(cbits, 1, nbytes, fout) != nbytes) {
|
||||||
|
snprintf(errstr, errlen, "could not write output data");
|
||||||
|
ret = false;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Squeeze out the last bits */
|
||||||
|
nbytes = speex_bits_write(&bits, cbits, 200);
|
||||||
|
if (fwrite(cbits, 1, nbytes, fout) != nbytes) {
|
||||||
|
snprintf(errstr, errlen, "could not write output data");
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (st != NULL)
|
||||||
|
speex_encoder_destroy(st);
|
||||||
|
speex_bits_destroy(&bits);
|
||||||
|
if (resampler != NULL)
|
||||||
|
speex_resampler_destroy(resampler);
|
||||||
|
if (in != NULL)
|
||||||
|
free(in);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
34
tools/rbspeex/rbspeex.h
Normal file
34
tools/rbspeex/rbspeex.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/**************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef RBSPEEX_H
|
||||||
|
#define RBSPEEX_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
unsigned int get_long_le(unsigned char *p);
|
||||||
|
bool get_wave_metadata(FILE *fd, int *numchan, int *bps, int *sr, int *numsamples);
|
||||||
|
bool encode_file(FILE *fin, FILE *fout, float quality, int complexity,
|
||||||
|
bool narrowband, float volume, char *errstr, size_t errlen);
|
||||||
|
|
||||||
|
void put_ushort_le(unsigned short x, unsigned char *out);
|
||||||
|
void put_uint_le(unsigned int x, unsigned char *out);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -20,25 +20,13 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
#include "rbspeex.h"
|
||||||
|
|
||||||
#define USAGE_TEXT \
|
#define USAGE_TEXT \
|
||||||
"Usage: rbspeexdec infile outfile\n"\
|
"Usage: rbspeexdec infile outfile\n"\
|
||||||
"rbspeexdec outputs mono 16 bit 16 kHz WAV files.\n"\
|
"rbspeexdec outputs mono 16 bit 16 kHz WAV files.\n"\
|
||||||
"WARNING: This tool will only decode files made with rbspeexenc!\n"
|
"WARNING: This tool will only decode files made with rbspeexenc!\n"
|
||||||
|
|
||||||
void put_ushort_le(unsigned short x, unsigned char *out)
|
|
||||||
{
|
|
||||||
out[0] = x & 0xff;
|
|
||||||
out[1] = x >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void put_uint_le(unsigned int x, unsigned char *out)
|
|
||||||
{
|
|
||||||
out[0] = x & 0xff;
|
|
||||||
out[1] = (x >> 8) & 0xff;
|
|
||||||
out[2] = (x >> 16) & 0xff;
|
|
||||||
out[3] = x >> 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
*
|
*
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include <speex/speex.h>
|
|
||||||
#include <speex/speex_resampler.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "rbspeex.h"
|
||||||
|
|
||||||
#define USAGE_TEXT \
|
#define USAGE_TEXT \
|
||||||
"Usage: rbspeexenc [options] infile outfile\n"\
|
"Usage: rbspeexenc [options] infile outfile\n"\
|
||||||
"Options:\n"\
|
"Options:\n"\
|
||||||
|
@ -35,222 +35,6 @@
|
||||||
"to either 16 kHz by default, or 8 kHz if narrowband mode is enabled.\n"\
|
"to either 16 kHz by default, or 8 kHz if narrowband mode is enabled.\n"\
|
||||||
"WARNING: This tool will create files that are only usable by Rockbox!\n"
|
"WARNING: This tool will create files that are only usable by Rockbox!\n"
|
||||||
|
|
||||||
/* 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 = fread(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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. Files are left open, so remember to close
|
|
||||||
* them.
|
|
||||||
*/
|
|
||||||
bool encode_file(FILE *fin, FILE *fout, float quality, int complexity,
|
|
||||||
bool narrowband, float volume, char *errstr, size_t errlen)
|
|
||||||
{
|
|
||||||
spx_int16_t *in = NULL, *inpos;
|
|
||||||
spx_int16_t enc_buf[640]; /* Max frame size */
|
|
||||||
char cbits[200];
|
|
||||||
void *st = NULL;
|
|
||||||
SpeexResamplerState *resampler = NULL;
|
|
||||||
SpeexBits bits;
|
|
||||||
int i, tmp, target_sr, numchan, bps, sr, numsamples, frame_size, lookahead;
|
|
||||||
int nbytes;
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) {
|
|
||||||
snprintf(errstr, errlen, "invalid WAV file");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (numchan != 1) {
|
|
||||||
snprintf(errstr, errlen, "input file must be mono");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (bps != 16) {
|
|
||||||
snprintf(errstr, errlen, "samples must be 16 bit");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate an encoder of specified type, defaults to wideband */
|
|
||||||
st = speex_encoder_init(narrowband ? &speex_nb_mode : &speex_wb_mode);
|
|
||||||
if (narrowband)
|
|
||||||
target_sr = 8000;
|
|
||||||
else
|
|
||||||
target_sr = 16000;
|
|
||||||
speex_bits_init(&bits);
|
|
||||||
|
|
||||||
/* VBR */
|
|
||||||
tmp = 1;
|
|
||||||
speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
|
|
||||||
/* Quality, 0-10 */
|
|
||||||
speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &quality);
|
|
||||||
/* Complexity, 0-10 */
|
|
||||||
speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
|
|
||||||
speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
|
|
||||||
speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
|
|
||||||
|
|
||||||
/* Read input samples into a buffer */
|
|
||||||
in = calloc(numsamples + lookahead, sizeof(spx_int16_t));
|
|
||||||
if (in == NULL) {
|
|
||||||
snprintf(errstr, errlen, "could not allocate clip memory");
|
|
||||||
ret = false;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
if (fread(in, 2, numsamples, fin) != numsamples) {
|
|
||||||
snprintf(errstr, errlen, "could not read input file data");
|
|
||||||
ret = false;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (volume != 1.0f) {
|
|
||||||
for (i = 0; i < numsamples; ++i)
|
|
||||||
in[i] *= volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sr != target_sr) {
|
|
||||||
resampler = speex_resampler_init(1, sr, target_sr, 10, NULL);
|
|
||||||
speex_resampler_skip_zeros(resampler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There will be 'lookahead' samples of zero at the end of the array, to
|
|
||||||
* make sure the Speex encoder is allowed to spit out all its data at clip
|
|
||||||
* end */
|
|
||||||
numsamples += lookahead;
|
|
||||||
|
|
||||||
inpos = in;
|
|
||||||
while (numsamples > 0) {
|
|
||||||
int samples = frame_size;
|
|
||||||
|
|
||||||
/* Check if we need to resample */
|
|
||||||
if (sr != target_sr) {
|
|
||||||
spx_uint32_t in_len = numsamples, out_len = frame_size;
|
|
||||||
double resample_factor = (double)sr/(double)target_sr;
|
|
||||||
/* Calculate how many input samples are needed for one full frame
|
|
||||||
* out, and add some, just in case. */
|
|
||||||
spx_uint32_t samples_in = frame_size*resample_factor + 50;
|
|
||||||
|
|
||||||
/* Limit this or resampler will try to allocate it all on stack */
|
|
||||||
if (in_len > samples_in)
|
|
||||||
in_len = samples_in;
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (speex_encode_int(st, enc_buf, &bits) < 0) {
|
|
||||||
snprintf(errstr, errlen, "encoder error");
|
|
||||||
ret = false;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
if (fwrite(cbits, 1, nbytes, fout) != nbytes) {
|
|
||||||
snprintf(errstr, errlen, "could not write output data");
|
|
||||||
ret = false;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Squeeze out the last bits */
|
|
||||||
nbytes = speex_bits_write(&bits, cbits, 200);
|
|
||||||
if (fwrite(cbits, 1, nbytes, fout) != nbytes) {
|
|
||||||
snprintf(errstr, errlen, "could not write output data");
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish:
|
|
||||||
if (st != NULL)
|
|
||||||
speex_encoder_destroy(st);
|
|
||||||
speex_bits_destroy(&bits);
|
|
||||||
if (resampler != NULL)
|
|
||||||
speex_resampler_destroy(resampler);
|
|
||||||
if (in != NULL)
|
|
||||||
free(in);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -308,3 +92,4 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue