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 35 /** 36 * Error handling 37 */ 38 39 static void error(int status, const char *format, ...) 40 { 41 va_list args; 42 43 fflush(stdout); 44 45 va_start(args, format); 46 vfprintf(stderr, format, args); 47 va_end(args); 48 49 fprintf(stderr, status ? ": %s\n" : "\n", strerror(status)); 50 exit(status); 51 } 52 53 54 /** 55 * Parameters 56 */ 57 58 struct parameters { 59 const char *fname_in; 60 const char *fname_out; 61 float frame_ms; 62 int srate_hz; 63 int bitrate; 64 }; 65 66 static struct parameters parse_args(int argc, char *argv[]) 67 { 68 static const char *usage = 69 "Usage: %s [options] [wav_file] [out_file]\n" 70 "\n" 71 "wav_file\t" "Input wave file, stdin if omitted\n" 72 "out_file\t" "Output bitstream file, stdout if omitted\n" 73 "\n" 74 "Options:\n" 75 "\t-h\t" "Display help\n" 76 "\t-b\t" "Bitrate in bps (mandatory)\n" 77 "\t-m\t" "Frame duration in ms (default 10)\n" 78 "\t-r\t" "Encoder samplerate (default is input samplerate)\n" 79 "\n"; 80 81 struct parameters p = { .frame_ms = 10 }; 82 83 for (int iarg = 1; iarg < argc; ) { 84 const char *arg = argv[iarg++]; 85 86 if (arg[0] == '-') { 87 if (arg[2] != '\0') 88 error(EINVAL, "Option %s", arg); 89 90 char opt = arg[1]; 91 const char *optarg; 92 93 switch (opt) { 94 case 'b': case 'm': case 'r': 95 if (iarg >= argc) 96 error(EINVAL, "Argument %s", arg); 97 optarg = argv[iarg++]; 98 } 99 100 switch (opt) { 101 case 'h': fprintf(stderr, usage, argv[0]); exit(0); 102 case 'b': p.bitrate = atoi(optarg); break; 103 case 'm': p.frame_ms = atof(optarg); break; 104 case 'r': p.srate_hz = atoi(optarg); break; 105 default: 106 error(EINVAL, "Option %s", arg); 107 } 108 109 } else { 110 111 if (!p.fname_in) 112 p.fname_in = arg; 113 else if (!p.fname_out) 114 p.fname_out = arg; 115 else 116 error(EINVAL, "Argument %s", arg); 117 } 118 } 119 120 return p; 121 } 122 123 124 /** 125 * Return time in (us) from unspecified point in the past 126 */ 127 128 static unsigned clock_us(void) 129 { 130 struct timespec ts; 131 132 clock_gettime(CLOCK_MONOTONIC, &ts); 133 134 return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000); 135 } 136 137 138 /** 139 * Entry point 140 */ 141 142 int main(int argc, char *argv[]) 143 { 144 /* --- Read parameters --- */ 145 146 struct parameters p = parse_args(argc, argv); 147 FILE *fp_in = stdin, *fp_out = stdout; 148 149 if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL) 150 error(errno, "%s", p.fname_in); 151 152 if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL) 153 error(errno, "%s", p.fname_out); 154 155 if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz)) 156 error(EINVAL, "Samplerate %d Hz", p.srate_hz); 157 158 /* --- Check parameters --- */ 159 160 int frame_us = p.frame_ms * 1000; 161 int srate_hz, nch, nsamples; 162 int pcm_sbits, pcm_sbytes; 163 164 if (wave_read_header(fp_in, 165 &pcm_sbits, &pcm_sbytes, &srate_hz, &nch, &nsamples) < 0) 166 error(EINVAL, "Bad or unsupported WAVE input file"); 167 168 if (p.bitrate <= 0) 169 error(EINVAL, "Bitrate"); 170 171 if (!LC3_CHECK_DT_US(frame_us)) 172 error(EINVAL, "Frame duration"); 173 174 if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz > srate_hz)) 175 error(EINVAL, "Samplerate %d Hz", srate_hz); 176 177 if (pcm_sbits != 16 && pcm_sbits != 24) 178 error(EINVAL, "Bitdepth %d", pcm_sbits); 179 180 if ((pcm_sbits == 16 && pcm_sbytes != 16/8) || 181 (pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8)) 182 error(EINVAL, "Sample storage on %d bytes", pcm_sbytes); 183 184 if (nch < 1 || nch > 2) 185 error(EINVAL, "Number of channels %d", nch); 186 187 int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz; 188 int enc_samples = !p.srate_hz ? nsamples : 189 ((int64_t)nsamples * enc_srate_hz) / srate_hz; 190 191 lc3bin_write_header(fp_out, 192 frame_us, enc_srate_hz, p.bitrate, nch, enc_samples); 193 194 /* --- Setup encoding --- */ 195 196 int frame_bytes = lc3_frame_bytes(frame_us, p.bitrate / nch); 197 int frame_samples = lc3_frame_samples(frame_us, srate_hz); 198 int encode_samples = nsamples + lc3_delay_samples(frame_us, srate_hz); 199 200 lc3_encoder_t enc[nch]; 201 int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes]; 202 enum lc3_pcm_format pcm_fmt = 203 pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 : 204 pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16; 205 uint8_t out[nch][frame_bytes]; 206 207 for (int ich = 0; ich < nch; ich++) 208 enc[ich] = lc3_setup_encoder(frame_us, enc_srate_hz, srate_hz, 209 malloc(lc3_encoder_size(frame_us, srate_hz))); 210 211 /* --- Encoding loop --- */ 212 213 static const char *dash_line = "========================================"; 214 215 int nsec = 0; 216 unsigned t0 = clock_us(); 217 218 for (int i = 0; i * frame_samples < encode_samples; i++) { 219 220 int nread = wave_read_pcm(fp_in, pcm_sbytes, nch, frame_samples, pcm); 221 222 memset(pcm + nread * nch * pcm_sbytes, 0, 223 nch * (frame_samples - nread) * pcm_sbytes); 224 225 if (floorf(i * frame_us * 1e-6) > nsec) { 226 float progress = fminf( 227 (float)i * frame_samples / encode_samples, 1); 228 229 fprintf(stderr, "%02d:%02d [%-40s]\r", 230 nsec / 60, nsec % 60, 231 dash_line + (int)floorf((1 - progress) * 40)); 232 233 nsec = (int)(i * frame_us * 1e-6); 234 } 235 236 for (int ich = 0; ich < nch; ich++) 237 lc3_encode(enc[ich], 238 pcm_fmt, pcm + ich * pcm_sbytes, nch, 239 frame_bytes, out[ich]); 240 241 lc3bin_write_data(fp_out, out, nch, frame_bytes); 242 } 243 244 unsigned t = (clock_us() - t0) / 1000; 245 nsec = encode_samples / srate_hz; 246 247 fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n", 248 nsec / 60, nsec % 60, t / 1000, t % 1000, ""); 249 250 /* --- Cleanup --- */ 251 252 for (int ich = 0; ich < nch; ich++) 253 free(enc[ich]); 254 255 if (fp_in != stdin) 256 fclose(fp_in); 257 258 if (fp_out != stdout) 259 fclose(fp_out); 260 } 261