blob: 7869602f44b7f6ad5555ed6ef858a272c096e680 [file] [log] [blame]
/**************************************************************************
* __________ __ ___.
* 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>
#define USAGE_TEXT \
"Usage: rbspeexenc [options] infile outfile\n"\
"Options:\n"\
" -q x Quality, floating point number in the range [0-10], default 8.0\n"\
" -c x Complexity, increases quality for a given bitrate, but encodes\n"\
" slower, range [0-10], default 3\n"\
" -n Enable narrowband mode, will resample input to 8 kHz\n\n"\
" -v x Volume, amplitude multiplier, default 1.0.\n"\
"rbspeexenc expects a mono 16 bit WAV file as input. Files will be resampled\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"
/* 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;
}
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;
int complexity = 3;
float quality = 8.f;
bool narrowband = false;
float volume = 1.0f;
int target_sr;
int numchan, bps, sr, numsamples;
int frame_size;
int lookahead;
if (argc < 3) {
printf(USAGE_TEXT);
return 1;
}
i = 1;
while (i < argc - 2) {
if (strncmp(argv[i], "-q", 2) == 0)
quality = atof(argv[++i]);
else if (strncmp(argv[i], "-c", 2) == 0)
complexity = atoi(argv[++i]);
else if (strncmp(argv[i], "-v", 2) == 0)
volume = atof(argv[++i]);
else if (strncmp(argv[i], "-n", 2) == 0)
narrowband = true;
++i;
}
/* 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;
/* 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.
*/
/* 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);
fin = fopen(argv[argc - 2], "rb");
if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) {
printf("Error: invalid WAV file\n");
return 1;
}
if (sr != target_sr) {
resampler = speex_resampler_init(1, sr, target_sr, 10, NULL);
speex_resampler_skip_zeros(resampler);
}
if (numchan != 1) {
printf("Error: input file must be mono\n");
return 1;
}
if (bps != 16) {
printf("Error: samples must be 16 bit\n");
return 1;
}
/* Read input samples into a buffer */
in = calloc(numsamples + lookahead, sizeof(spx_int16_t));
if (in == NULL) {
printf("Error: could not allocate clip memory\n");
return 1;
}
fread(in, 2, numsamples, fin);
fclose(fin);
if(volume != 1.0f) {
for(i=0; i<numsamples; i++)
in[i] *= volume;
}
/* 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;
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 != 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) {
printf("Error: encoder error\n");
return 1;
}
/* 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;
}