1 /****************************************************************************** 2 * 3 * Copyright 2022 Google LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19 #define _POSIX_C_SOURCE 199309L 20 21 #include <stdalign.h> 22 #include <stdio.h> 23 #include <stdarg.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <math.h> 27 #include <time.h> 28 #include <errno.h> 29 30 #include <lc3.h> 31 #include "lc3bin.h" 32 #include "wave.h" 33 34 #define MAX_CHANNELS 2 35 36 37 /** 38 * Error handling 39 */ 40 41 static void error(int status, const char *format, ...) 42 { 43 va_list args; 44 45 fflush(stdout); 46 47 va_start(args, format); 48 vfprintf(stderr, format, args); 49 va_end(args); 50 51 fprintf(stderr, status ? ": %s\n" : "\n", strerror(status)); 52 exit(status); 53 } 54 55 56 /** 57 * Parameters 58 */ 59 60 struct parameters { 61 const char *fname_in; 62 const char *fname_out; 63 float frame_ms; 64 int srate_hz; 65 bool hrmode; 66 int bitrate; 67 }; 68 69 static struct parameters parse_args(int argc, char *argv[]) 70 { 71 static const char *usage = 72 "Usage: %s [options] [wav_file] [out_file]\n" 73 "\n" 74 "wav_file\t" "Input wave file, stdin if omitted\n" 75 "out_file\t" "Output bitstream file, stdout if omitted\n" 76 "\n" 77 "Options:\n" 78 "\t-h\t" "Display help\n" 79 "\t-b\t" "Bitrate in bps (mandatory)\n" 80 "\t-m\t" "Frame duration in ms (default 10)\n" 81 "\t-r\t" "Encoder samplerate (default is input samplerate)\n" 82 "\t-H\t" "Enable high-resolution mode\n" 83 "\n"; 84 85 struct parameters p = { .frame_ms = 10 }; 86 87 for (int iarg = 1; iarg < argc; ) { 88 const char *arg = argv[iarg++]; 89 90 if (arg[0] == '-') { 91 if (arg[2] != '\0') 92 error(EINVAL, "Option %s", arg); 93 94 char opt = arg[1]; 95 const char *optarg = NULL; 96 97 switch (opt) { 98 case 'b': case 'm': case 'r': 99 if (iarg >= argc) 100 error(EINVAL, "Argument %s", arg); 101 optarg = argv[iarg++]; 102 } 103 104 switch (opt) { 105 case 'h': fprintf(stderr, usage, argv[0]); exit(0); 106 case 'b': p.bitrate = atoi(optarg); break; 107 case 'm': p.frame_ms = atof(optarg); break; 108 case 'r': p.srate_hz = atoi(optarg); break; 109 case 'H': p.hrmode = true; break; 110 default: 111 error(EINVAL, "Option %s", arg); 112 } 113 114 } else { 115 116 if (!p.fname_in) 117 p.fname_in = arg; 118 else if (!p.fname_out) 119 p.fname_out = arg; 120 else 121 error(EINVAL, "Argument %s", arg); 122 } 123 } 124 125 return p; 126 } 127 128 129 /** 130 * Return time in (us) from unspecified point in the past 131 */ 132 133 static unsigned clock_us(void) 134 { 135 struct timespec ts; 136 137 clock_gettime(CLOCK_MONOTONIC, &ts); 138 139 return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000); 140 } 141 142 143 /** 144 * Entry point 145 */ 146 147 int main(int argc, char *argv[]) 148 { 149 /* --- Read parameters --- */ 150 151 struct parameters p = parse_args(argc, argv); 152 FILE *fp_in = stdin, *fp_out = stdout; 153 154 if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL) 155 error(errno, "%s", p.fname_in); 156 157 if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL) 158 error(errno, "%s", p.fname_out); 159 160 /* --- Check parameters --- */ 161 162 int frame_us = p.frame_ms * 1000; 163 int srate_hz, nchannels, nsamples; 164 int pcm_sbits, pcm_sbytes; 165 166 if (wave_read_header(fp_in, 167 &pcm_sbits, &pcm_sbytes, &srate_hz, &nchannels, &nsamples) < 0) 168 error(EINVAL, "Bad or unsupported WAVE input file"); 169 170 if (p.bitrate <= 0) 171 error(EINVAL, "Bitrate"); 172 173 if (!LC3_CHECK_DT_US(frame_us)) 174 error(EINVAL, "Frame duration"); 175 176 if (!LC3_HR_CHECK_SR_HZ(p.hrmode, srate_hz)) 177 error(EINVAL, "Samplerate %d Hz", srate_hz); 178 179 if (pcm_sbits != 16 && pcm_sbits != 24) 180 error(EINVAL, "Bitdepth %d", pcm_sbits); 181 182 if ((pcm_sbits == 16 && pcm_sbytes != 16/8) || 183 (pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8)) 184 error(EINVAL, "Sample storage on %d bytes", pcm_sbytes); 185 186 if (nchannels < 1 || nchannels > MAX_CHANNELS) 187 error(EINVAL, "Number of channels %d", nchannels); 188 189 if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(p.hrmode, p.srate_hz) || 190 p.srate_hz > srate_hz )) 191 error(EINVAL, "Encoder samplerate %d Hz", p.srate_hz); 192 193 int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz; 194 int enc_samples = !p.srate_hz ? nsamples : 195 ((int64_t)nsamples * enc_srate_hz) / srate_hz; 196 197 int block_bytes = lc3_hr_frame_block_bytes( 198 p.hrmode, frame_us, srate_hz, nchannels, p.bitrate); 199 200 int bitrate = lc3_hr_resolve_bitrate( 201 p.hrmode, frame_us, srate_hz, block_bytes); 202 203 if (bitrate != p.bitrate) 204 fprintf(stderr, "Bitrate adjusted to %d bps\n", bitrate); 205 206 lc3bin_write_header(fp_out, 207 frame_us, enc_srate_hz, p.hrmode, 208 bitrate, nchannels, enc_samples); 209 210 /* --- Setup encoding --- */ 211 212 int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4]; 213 uint8_t out[2 * LC3_HR_MAX_FRAME_BYTES]; 214 lc3_encoder_t enc[2]; 215 216 int frame_samples = lc3_hr_frame_samples( 217 p.hrmode, frame_us, srate_hz); 218 int encode_samples = nsamples + lc3_hr_delay_samples( 219 p.hrmode, frame_us, srate_hz); 220 enum lc3_pcm_format pcm_fmt = 221 pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 : 222 pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16; 223 224 for (int ich = 0; ich < nchannels; ich++) { 225 enc[ich] = lc3_hr_setup_encoder( 226 p.hrmode, frame_us, enc_srate_hz, srate_hz, 227 malloc(lc3_hr_encoder_size(p.hrmode, frame_us, srate_hz))); 228 229 if (!enc[ich]) 230 error(EINVAL, "Encoder initialization failed"); 231 } 232 233 /* --- Encoding loop --- */ 234 235 static const char *dash_line = "========================================"; 236 237 int nsec = 0; 238 unsigned t0 = clock_us(); 239 240 for (int i = 0; i * frame_samples < encode_samples; i++) { 241 242 int nread = wave_read_pcm(fp_in, pcm_sbytes, nchannels, frame_samples, pcm); 243 244 memset(pcm + nread * nchannels * pcm_sbytes, 0, 245 nchannels * (frame_samples - nread) * pcm_sbytes); 246 247 if (floorf(i * frame_us * 1e-6) > nsec) { 248 float progress = fminf( 249 (float)i * frame_samples / encode_samples, 1); 250 251 fprintf(stderr, "%02d:%02d [%-40s]\r", 252 nsec / 60, nsec % 60, 253 dash_line + (int)floorf((1 - progress) * 40)); 254 255 nsec = (int)(i * frame_us * 1e-6); 256 } 257 258 uint8_t *out_ptr = out; 259 for (int ich = 0; ich < nchannels; ich++) { 260 int frame_bytes = block_bytes / nchannels 261 + (ich < block_bytes % nchannels); 262 263 lc3_encode(enc[ich], 264 pcm_fmt, pcm + ich * pcm_sbytes, nchannels, 265 frame_bytes, out_ptr); 266 267 out_ptr += frame_bytes; 268 } 269 270 lc3bin_write_data(fp_out, out, block_bytes); 271 } 272 273 unsigned t = (clock_us() - t0) / 1000; 274 nsec = encode_samples / srate_hz; 275 276 fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n", 277 nsec / 60, nsec % 60, t / 1000, t % 1000, ""); 278 279 /* --- Cleanup --- */ 280 281 for (int ich = 0; ich < nchannels; ich++) 282 free(enc[ich]); 283 284 if (fp_in != stdin) 285 fclose(fp_in); 286 287 if (fp_out != stdout) 288 fclose(fp_out); 289 } 290