Dominik Wenger | ed047d9 | 2007-12-14 16:04:38 +0000 | [diff] [blame] | 1 | /************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * |
| 9 | * Copyright (C) 2007 Thom Johansen |
| 10 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 11 | * This program is free software; you can redistribute it and/or |
| 12 | * modify it under the terms of the GNU General Public License |
| 13 | * as published by the Free Software Foundation; either version 2 |
| 14 | * of the License, or (at your option) any later version. |
Dominik Wenger | ed047d9 | 2007-12-14 16:04:38 +0000 | [diff] [blame] | 15 | * |
| 16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 17 | * KIND, either express or implied. |
| 18 | * |
| 19 | ***************************************************************************/ |
| 20 | |
| 21 | #include <speex/speex.h> |
| 22 | #include <speex/speex_resampler.h> |
| 23 | #include <stdio.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <stdbool.h> |
| 27 | |
| 28 | #include "rbspeex.h" |
| 29 | |
| 30 | /* Read an unaligned 32-bit little endian long from buffer. */ |
| 31 | unsigned int get_long_le(unsigned char *p) |
| 32 | { |
| 33 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); |
| 34 | } |
| 35 | |
| 36 | void put_ushort_le(unsigned short x, unsigned char *out) |
| 37 | { |
| 38 | out[0] = x & 0xff; |
| 39 | out[1] = x >> 8; |
| 40 | } |
| 41 | |
| 42 | void put_uint_le(unsigned int x, unsigned char *out) |
| 43 | { |
| 44 | out[0] = x & 0xff; |
| 45 | out[1] = (x >> 8) & 0xff; |
| 46 | out[2] = (x >> 16) & 0xff; |
| 47 | out[3] = x >> 24; |
| 48 | } |
| 49 | |
| 50 | |
| 51 | |
| 52 | bool get_wave_metadata(FILE *fd, int *numchan, int *bps, int *sr, int *numsamples) |
| 53 | { |
| 54 | unsigned char buf[1024]; |
| 55 | unsigned long totalsamples = 0; |
| 56 | unsigned long channels = 0; |
| 57 | unsigned long bitspersample = 0; |
| 58 | unsigned long numbytes = 0; |
| 59 | size_t read_bytes; |
| 60 | int i; |
| 61 | |
| 62 | if ((read_bytes = fread(buf, 1, 12, fd)) < 12) |
| 63 | return false; |
| 64 | |
| 65 | if ((memcmp(buf, "RIFF",4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) |
| 66 | return false; |
| 67 | |
| 68 | /* iterate over WAVE chunks until 'data' chunk */ |
| 69 | while (1) { |
| 70 | /* get chunk header */ |
| 71 | if ((read_bytes = fread(buf, 1, 8, fd)) < 8) |
| 72 | return false; |
| 73 | |
| 74 | /* chunkSize */ |
| 75 | i = get_long_le(&buf[4]); |
| 76 | |
| 77 | if (memcmp(buf, "fmt ", 4) == 0) { |
| 78 | /* get rest of chunk */ |
| 79 | if ((read_bytes = fread(buf, 1, 16, fd)) < 16) |
| 80 | return false; |
| 81 | |
| 82 | i -= 16; |
| 83 | |
| 84 | channels = *numchan = buf[2] | (buf[3] << 8); |
| 85 | *sr = get_long_le(&buf[4]); |
| 86 | /* wBitsPerSample */ |
| 87 | bitspersample = *bps = buf[14] | (buf[15] << 8); |
| 88 | } else if (memcmp(buf, "data", 4) == 0) { |
| 89 | numbytes = i; |
| 90 | break; |
| 91 | } else if (memcmp(buf, "fact", 4) == 0) { |
| 92 | /* dwSampleLength */ |
| 93 | if (i >= 4) { |
| 94 | /* get rest of chunk */ |
| 95 | if ((read_bytes = fread(buf, 1, 4, fd)) < 4) |
| 96 | return false; |
| 97 | |
| 98 | i -= 4; |
| 99 | totalsamples = get_long_le(buf); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /* seek to next chunk (even chunk sizes must be padded) */ |
| 104 | if (i & 0x01) |
| 105 | i++; |
| 106 | |
| 107 | if (fseek(fd, i, SEEK_CUR) < 0) |
| 108 | return false; |
| 109 | } |
| 110 | |
| 111 | if ((numbytes == 0) || (channels == 0)) |
| 112 | return false; |
| 113 | |
| 114 | if (totalsamples == 0) { |
| 115 | /* for PCM only */ |
| 116 | totalsamples = numbytes/((((bitspersample - 1) / 8) + 1)*channels); |
| 117 | } |
| 118 | *numsamples = totalsamples; |
| 119 | return true; |
| 120 | } |
| 121 | |
| 122 | /* We'll eat an entire WAV file here, and encode it with Speex, packing the |
| 123 | * bits as tightly as we can. Output is completely raw, with absolutely |
| 124 | * nothing to identify the contents. Files are left open, so remember to close |
| 125 | * them. |
| 126 | */ |
| 127 | bool encode_file(FILE *fin, FILE *fout, float quality, int complexity, |
| 128 | bool narrowband, float volume, char *errstr, size_t errlen) |
| 129 | { |
| 130 | spx_int16_t *in = NULL, *inpos; |
| 131 | spx_int16_t enc_buf[640]; /* Max frame size */ |
| 132 | char cbits[200]; |
| 133 | void *st = NULL; |
| 134 | SpeexResamplerState *resampler = NULL; |
| 135 | SpeexBits bits; |
| 136 | int i, tmp, target_sr, numchan, bps, sr, numsamples, frame_size, lookahead; |
| 137 | int nbytes; |
| 138 | bool ret = true; |
| 139 | |
| 140 | if (!get_wave_metadata(fin, &numchan, &bps, &sr, &numsamples)) { |
| 141 | snprintf(errstr, errlen, "invalid WAV file"); |
| 142 | return false; |
| 143 | } |
| 144 | if (numchan != 1) { |
| 145 | snprintf(errstr, errlen, "input file must be mono"); |
| 146 | return false; |
| 147 | } |
| 148 | if (bps != 16) { |
| 149 | snprintf(errstr, errlen, "samples must be 16 bit"); |
| 150 | return false; |
| 151 | } |
| 152 | |
| 153 | /* Allocate an encoder of specified type, defaults to wideband */ |
| 154 | st = speex_encoder_init(narrowband ? &speex_nb_mode : &speex_wb_mode); |
| 155 | if (narrowband) |
| 156 | target_sr = 8000; |
| 157 | else |
| 158 | target_sr = 16000; |
| 159 | speex_bits_init(&bits); |
| 160 | |
| 161 | /* VBR */ |
| 162 | tmp = 1; |
| 163 | speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); |
| 164 | /* Quality, 0-10 */ |
| 165 | speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &quality); |
| 166 | /* Complexity, 0-10 */ |
| 167 | speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity); |
| 168 | speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size); |
| 169 | speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead); |
| 170 | |
| 171 | /* Read input samples into a buffer */ |
| 172 | in = calloc(numsamples + lookahead, sizeof(spx_int16_t)); |
| 173 | if (in == NULL) { |
| 174 | snprintf(errstr, errlen, "could not allocate clip memory"); |
| 175 | ret = false; |
| 176 | goto finish; |
| 177 | } |
| 178 | if (fread(in, 2, numsamples, fin) != numsamples) { |
| 179 | snprintf(errstr, errlen, "could not read input file data"); |
| 180 | ret = false; |
| 181 | goto finish; |
| 182 | } |
| 183 | |
| 184 | if (volume != 1.0f) { |
| 185 | for (i = 0; i < numsamples; ++i) |
| 186 | in[i] *= volume; |
| 187 | } |
| 188 | |
| 189 | if (sr != target_sr) { |
| 190 | resampler = speex_resampler_init(1, sr, target_sr, 10, NULL); |
| 191 | speex_resampler_skip_zeros(resampler); |
| 192 | } |
| 193 | |
| 194 | /* There will be 'lookahead' samples of zero at the end of the array, to |
| 195 | * make sure the Speex encoder is allowed to spit out all its data at clip |
| 196 | * end */ |
| 197 | numsamples += lookahead; |
| 198 | |
| 199 | inpos = in; |
| 200 | while (numsamples > 0) { |
| 201 | int samples = frame_size; |
| 202 | |
| 203 | /* Check if we need to resample */ |
| 204 | if (sr != target_sr) { |
| 205 | spx_uint32_t in_len = numsamples, out_len = frame_size; |
| 206 | double resample_factor = (double)sr/(double)target_sr; |
| 207 | /* Calculate how many input samples are needed for one full frame |
| 208 | * out, and add some, just in case. */ |
| 209 | spx_uint32_t samples_in = frame_size*resample_factor + 50; |
| 210 | |
| 211 | /* Limit this or resampler will try to allocate it all on stack */ |
| 212 | if (in_len > samples_in) |
| 213 | in_len = samples_in; |
| 214 | speex_resampler_process_int(resampler, 0, inpos, &in_len, |
| 215 | enc_buf, &out_len); |
| 216 | inpos += in_len; |
| 217 | samples = out_len; |
| 218 | numsamples -= in_len; |
| 219 | } else { |
| 220 | if (samples > numsamples) |
| 221 | samples = numsamples; |
| 222 | memcpy(enc_buf, inpos, samples*2); |
| 223 | inpos += frame_size; |
| 224 | numsamples -= frame_size; |
| 225 | } |
| 226 | /* Pad out with zeros if we didn't fill all input */ |
| 227 | memset(enc_buf + samples, 0, (frame_size - samples)*2); |
| 228 | |
| 229 | if (speex_encode_int(st, enc_buf, &bits) < 0) { |
| 230 | snprintf(errstr, errlen, "encoder error"); |
| 231 | ret = false; |
| 232 | goto finish; |
| 233 | } |
| 234 | |
| 235 | /* Copy the bits to an array of char that can be written */ |
| 236 | nbytes = speex_bits_write_whole_bytes(&bits, cbits, 200); |
| 237 | |
| 238 | /* Write the compressed data */ |
| 239 | if (fwrite(cbits, 1, nbytes, fout) != nbytes) { |
| 240 | snprintf(errstr, errlen, "could not write output data"); |
| 241 | ret = false; |
| 242 | goto finish; |
| 243 | } |
| 244 | } |
| 245 | /* Squeeze out the last bits */ |
| 246 | nbytes = speex_bits_write(&bits, cbits, 200); |
| 247 | if (fwrite(cbits, 1, nbytes, fout) != nbytes) { |
| 248 | snprintf(errstr, errlen, "could not write output data"); |
| 249 | ret = false; |
| 250 | } |
| 251 | |
| 252 | finish: |
| 253 | if (st != NULL) |
| 254 | speex_encoder_destroy(st); |
| 255 | speex_bits_destroy(&bits); |
| 256 | if (resampler != NULL) |
| 257 | speex_resampler_destroy(resampler); |
| 258 | if (in != NULL) |
| 259 | free(in); |
| 260 | return ret; |
| 261 | } |
| 262 | |
| 263 | |