14930cef6SMatthias Ringwald /****************************************************************************** 24930cef6SMatthias Ringwald * 34930cef6SMatthias Ringwald * Copyright 2022 Google LLC 44930cef6SMatthias Ringwald * 54930cef6SMatthias Ringwald * Licensed under the Apache License, Version 2.0 (the "License"); 64930cef6SMatthias Ringwald * you may not use this file except in compliance with the License. 74930cef6SMatthias Ringwald * You may obtain a copy of the License at: 84930cef6SMatthias Ringwald * 94930cef6SMatthias Ringwald * http://www.apache.org/licenses/LICENSE-2.0 104930cef6SMatthias Ringwald * 114930cef6SMatthias Ringwald * Unless required by applicable law or agreed to in writing, software 124930cef6SMatthias Ringwald * distributed under the License is distributed on an "AS IS" BASIS, 134930cef6SMatthias Ringwald * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 144930cef6SMatthias Ringwald * See the License for the specific language governing permissions and 154930cef6SMatthias Ringwald * limitations under the License. 164930cef6SMatthias Ringwald * 174930cef6SMatthias Ringwald ******************************************************************************/ 184930cef6SMatthias Ringwald 194930cef6SMatthias Ringwald #define _POSIX_C_SOURCE 199309L 204930cef6SMatthias Ringwald 214930cef6SMatthias Ringwald #include <stdalign.h> 224930cef6SMatthias Ringwald #include <stdio.h> 234930cef6SMatthias Ringwald #include <stdarg.h> 244930cef6SMatthias Ringwald #include <stdlib.h> 254930cef6SMatthias Ringwald #include <string.h> 264930cef6SMatthias Ringwald #include <math.h> 274930cef6SMatthias Ringwald #include <time.h> 284930cef6SMatthias Ringwald #include <errno.h> 294930cef6SMatthias Ringwald 304930cef6SMatthias Ringwald #include <lc3.h> 314930cef6SMatthias Ringwald #include "lc3bin.h" 324930cef6SMatthias Ringwald #include "wave.h" 334930cef6SMatthias Ringwald 344930cef6SMatthias Ringwald 354930cef6SMatthias Ringwald /** 364930cef6SMatthias Ringwald * Error handling 374930cef6SMatthias Ringwald */ 384930cef6SMatthias Ringwald 394930cef6SMatthias Ringwald static void error(int status, const char *format, ...) 404930cef6SMatthias Ringwald { 414930cef6SMatthias Ringwald va_list args; 424930cef6SMatthias Ringwald 434930cef6SMatthias Ringwald fflush(stdout); 444930cef6SMatthias Ringwald 454930cef6SMatthias Ringwald va_start(args, format); 464930cef6SMatthias Ringwald vfprintf(stderr, format, args); 474930cef6SMatthias Ringwald va_end(args); 484930cef6SMatthias Ringwald 494930cef6SMatthias Ringwald fprintf(stderr, status ? ": %s\n" : "\n", strerror(status)); 504930cef6SMatthias Ringwald exit(status); 514930cef6SMatthias Ringwald } 524930cef6SMatthias Ringwald 534930cef6SMatthias Ringwald 544930cef6SMatthias Ringwald /** 554930cef6SMatthias Ringwald * Parameters 564930cef6SMatthias Ringwald */ 574930cef6SMatthias Ringwald 584930cef6SMatthias Ringwald struct parameters { 594930cef6SMatthias Ringwald const char *fname_in; 604930cef6SMatthias Ringwald const char *fname_out; 614930cef6SMatthias Ringwald float frame_ms; 624930cef6SMatthias Ringwald int srate_hz; 634930cef6SMatthias Ringwald int bitrate; 644930cef6SMatthias Ringwald }; 654930cef6SMatthias Ringwald 664930cef6SMatthias Ringwald static struct parameters parse_args(int argc, char *argv[]) 674930cef6SMatthias Ringwald { 684930cef6SMatthias Ringwald static const char *usage = 694930cef6SMatthias Ringwald "Usage: %s [options] [wav_file] [out_file]\n" 704930cef6SMatthias Ringwald "\n" 714930cef6SMatthias Ringwald "wav_file\t" "Input wave file, stdin if omitted\n" 724930cef6SMatthias Ringwald "out_file\t" "Output bitstream file, stdout if omitted\n" 734930cef6SMatthias Ringwald "\n" 744930cef6SMatthias Ringwald "Options:\n" 754930cef6SMatthias Ringwald "\t-h\t" "Display help\n" 764930cef6SMatthias Ringwald "\t-b\t" "Bitrate in bps (mandatory)\n" 774930cef6SMatthias Ringwald "\t-m\t" "Frame duration in ms (default 10)\n" 784930cef6SMatthias Ringwald "\t-r\t" "Encoder samplerate (default is input samplerate)\n" 794930cef6SMatthias Ringwald "\n"; 804930cef6SMatthias Ringwald 814930cef6SMatthias Ringwald struct parameters p = { .frame_ms = 10 }; 824930cef6SMatthias Ringwald 834930cef6SMatthias Ringwald for (int iarg = 1; iarg < argc; ) { 844930cef6SMatthias Ringwald const char *arg = argv[iarg++]; 854930cef6SMatthias Ringwald 864930cef6SMatthias Ringwald if (arg[0] == '-') { 874930cef6SMatthias Ringwald if (arg[2] != '\0') 884930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg); 894930cef6SMatthias Ringwald 904930cef6SMatthias Ringwald char opt = arg[1]; 914930cef6SMatthias Ringwald const char *optarg; 924930cef6SMatthias Ringwald 934930cef6SMatthias Ringwald switch (opt) { 944930cef6SMatthias Ringwald case 'b': case 'm': case 'r': 954930cef6SMatthias Ringwald if (iarg >= argc) 964930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg); 974930cef6SMatthias Ringwald optarg = argv[iarg++]; 984930cef6SMatthias Ringwald } 994930cef6SMatthias Ringwald 1004930cef6SMatthias Ringwald switch (opt) { 1014930cef6SMatthias Ringwald case 'h': fprintf(stderr, usage, argv[0]); exit(0); 1024930cef6SMatthias Ringwald case 'b': p.bitrate = atoi(optarg); break; 1034930cef6SMatthias Ringwald case 'm': p.frame_ms = atof(optarg); break; 1044930cef6SMatthias Ringwald case 'r': p.srate_hz = atoi(optarg); break; 1054930cef6SMatthias Ringwald default: 1064930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg); 1074930cef6SMatthias Ringwald } 1084930cef6SMatthias Ringwald 1094930cef6SMatthias Ringwald } else { 1104930cef6SMatthias Ringwald 1114930cef6SMatthias Ringwald if (!p.fname_in) 1124930cef6SMatthias Ringwald p.fname_in = arg; 1134930cef6SMatthias Ringwald else if (!p.fname_out) 1144930cef6SMatthias Ringwald p.fname_out = arg; 1154930cef6SMatthias Ringwald else 1164930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg); 1174930cef6SMatthias Ringwald } 1184930cef6SMatthias Ringwald } 1194930cef6SMatthias Ringwald 1204930cef6SMatthias Ringwald return p; 1214930cef6SMatthias Ringwald } 1224930cef6SMatthias Ringwald 1234930cef6SMatthias Ringwald 1244930cef6SMatthias Ringwald /** 1254930cef6SMatthias Ringwald * Return time in (us) from unspecified point in the past 1264930cef6SMatthias Ringwald */ 1274930cef6SMatthias Ringwald 1284930cef6SMatthias Ringwald static unsigned clock_us(void) 1294930cef6SMatthias Ringwald { 1304930cef6SMatthias Ringwald struct timespec ts; 1314930cef6SMatthias Ringwald 1324930cef6SMatthias Ringwald clock_gettime(CLOCK_MONOTONIC, &ts); 1334930cef6SMatthias Ringwald 1344930cef6SMatthias Ringwald return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000); 1354930cef6SMatthias Ringwald } 1364930cef6SMatthias Ringwald 1374930cef6SMatthias Ringwald 1384930cef6SMatthias Ringwald /** 1394930cef6SMatthias Ringwald * Entry point 1404930cef6SMatthias Ringwald */ 1414930cef6SMatthias Ringwald 1424930cef6SMatthias Ringwald int main(int argc, char *argv[]) 1434930cef6SMatthias Ringwald { 1444930cef6SMatthias Ringwald /* --- Read parameters --- */ 1454930cef6SMatthias Ringwald 1464930cef6SMatthias Ringwald struct parameters p = parse_args(argc, argv); 1474930cef6SMatthias Ringwald FILE *fp_in = stdin, *fp_out = stdout; 1484930cef6SMatthias Ringwald 1494930cef6SMatthias Ringwald if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL) 1504930cef6SMatthias Ringwald error(errno, "%s", p.fname_in); 1514930cef6SMatthias Ringwald 1524930cef6SMatthias Ringwald if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL) 1534930cef6SMatthias Ringwald error(errno, "%s", p.fname_out); 1544930cef6SMatthias Ringwald 1554930cef6SMatthias Ringwald if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz)) 1564930cef6SMatthias Ringwald error(EINVAL, "Samplerate %d Hz", p.srate_hz); 1574930cef6SMatthias Ringwald 1584930cef6SMatthias Ringwald /* --- Check parameters --- */ 1594930cef6SMatthias Ringwald 1604930cef6SMatthias Ringwald int frame_us = p.frame_ms * 1000; 1614930cef6SMatthias Ringwald int srate_hz, nch, nsamples; 1624930cef6SMatthias Ringwald int pcm_sbits, pcm_sbytes; 1634930cef6SMatthias Ringwald 1644930cef6SMatthias Ringwald if (wave_read_header(fp_in, 1654930cef6SMatthias Ringwald &pcm_sbits, &pcm_sbytes, &srate_hz, &nch, &nsamples) < 0) 1664930cef6SMatthias Ringwald error(EINVAL, "Bad or unsupported WAVE input file"); 1674930cef6SMatthias Ringwald 1684930cef6SMatthias Ringwald if (p.bitrate <= 0) 1694930cef6SMatthias Ringwald error(EINVAL, "Bitrate"); 1704930cef6SMatthias Ringwald 1714930cef6SMatthias Ringwald if (!LC3_CHECK_DT_US(frame_us)) 1724930cef6SMatthias Ringwald error(EINVAL, "Frame duration"); 1734930cef6SMatthias Ringwald 1744930cef6SMatthias Ringwald if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz > srate_hz)) 1754930cef6SMatthias Ringwald error(EINVAL, "Samplerate %d Hz", srate_hz); 1764930cef6SMatthias Ringwald 1774930cef6SMatthias Ringwald if (pcm_sbits != 16 && pcm_sbits != 24) 1784930cef6SMatthias Ringwald error(EINVAL, "Bitdepth %d", pcm_sbits); 1794930cef6SMatthias Ringwald 180*4c4eb519SMatthias Ringwald if ((pcm_sbits == 16 && pcm_sbytes != 16/8) || 181*4c4eb519SMatthias Ringwald (pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8)) 1824930cef6SMatthias Ringwald error(EINVAL, "Sample storage on %d bytes", pcm_sbytes); 1834930cef6SMatthias Ringwald 1844930cef6SMatthias Ringwald if (nch < 1 || nch > 2) 1854930cef6SMatthias Ringwald error(EINVAL, "Number of channels %d", nch); 1864930cef6SMatthias Ringwald 1874930cef6SMatthias Ringwald int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz; 1884930cef6SMatthias Ringwald int enc_samples = !p.srate_hz ? nsamples : 1894930cef6SMatthias Ringwald ((int64_t)nsamples * enc_srate_hz) / srate_hz; 1904930cef6SMatthias Ringwald 1914930cef6SMatthias Ringwald lc3bin_write_header(fp_out, 1924930cef6SMatthias Ringwald frame_us, enc_srate_hz, p.bitrate, nch, enc_samples); 1934930cef6SMatthias Ringwald 1944930cef6SMatthias Ringwald /* --- Setup encoding --- */ 1954930cef6SMatthias Ringwald 1964930cef6SMatthias Ringwald int frame_bytes = lc3_frame_bytes(frame_us, p.bitrate / nch); 1974930cef6SMatthias Ringwald int frame_samples = lc3_frame_samples(frame_us, srate_hz); 1984930cef6SMatthias Ringwald int encode_samples = nsamples + lc3_delay_samples(frame_us, srate_hz); 1994930cef6SMatthias Ringwald 2004930cef6SMatthias Ringwald lc3_encoder_t enc[nch]; 2014930cef6SMatthias Ringwald int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes]; 2024930cef6SMatthias Ringwald enum lc3_pcm_format pcm_fmt = 203*4c4eb519SMatthias Ringwald pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 : 204*4c4eb519SMatthias Ringwald pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16; 2054930cef6SMatthias Ringwald uint8_t out[nch][frame_bytes]; 2064930cef6SMatthias Ringwald 2074930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2084930cef6SMatthias Ringwald enc[ich] = lc3_setup_encoder(frame_us, enc_srate_hz, srate_hz, 2094930cef6SMatthias Ringwald malloc(lc3_encoder_size(frame_us, srate_hz))); 2104930cef6SMatthias Ringwald 2114930cef6SMatthias Ringwald /* --- Encoding loop --- */ 2124930cef6SMatthias Ringwald 2134930cef6SMatthias Ringwald static const char *dash_line = "========================================"; 2144930cef6SMatthias Ringwald 2154930cef6SMatthias Ringwald int nsec = 0; 2164930cef6SMatthias Ringwald unsigned t0 = clock_us(); 2174930cef6SMatthias Ringwald 2184930cef6SMatthias Ringwald for (int i = 0; i * frame_samples < encode_samples; i++) { 2194930cef6SMatthias Ringwald 2204930cef6SMatthias Ringwald int nread = wave_read_pcm(fp_in, pcm_sbytes, nch, frame_samples, pcm); 2214930cef6SMatthias Ringwald 222*4c4eb519SMatthias Ringwald memset(pcm + nread * nch * pcm_sbytes, 0, 2234930cef6SMatthias Ringwald nch * (frame_samples - nread) * pcm_sbytes); 2244930cef6SMatthias Ringwald 2254930cef6SMatthias Ringwald if (floorf(i * frame_us * 1e-6) > nsec) { 2264930cef6SMatthias Ringwald float progress = fminf( 2274930cef6SMatthias Ringwald (float)i * frame_samples / encode_samples, 1); 2284930cef6SMatthias Ringwald 2294930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d [%-40s]\r", 2304930cef6SMatthias Ringwald nsec / 60, nsec % 60, 2314930cef6SMatthias Ringwald dash_line + (int)floorf((1 - progress) * 40)); 2324930cef6SMatthias Ringwald 2334930cef6SMatthias Ringwald nsec = (int)(i * frame_us * 1e-6); 2344930cef6SMatthias Ringwald } 2354930cef6SMatthias Ringwald 2364930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2374930cef6SMatthias Ringwald lc3_encode(enc[ich], 2384930cef6SMatthias Ringwald pcm_fmt, pcm + ich * pcm_sbytes, nch, 2394930cef6SMatthias Ringwald frame_bytes, out[ich]); 2404930cef6SMatthias Ringwald 2414930cef6SMatthias Ringwald lc3bin_write_data(fp_out, out, nch, frame_bytes); 2424930cef6SMatthias Ringwald } 2434930cef6SMatthias Ringwald 2444930cef6SMatthias Ringwald unsigned t = (clock_us() - t0) / 1000; 2454930cef6SMatthias Ringwald nsec = encode_samples / srate_hz; 2464930cef6SMatthias Ringwald 2474930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n", 2484930cef6SMatthias Ringwald nsec / 60, nsec % 60, t / 1000, t % 1000, ""); 2494930cef6SMatthias Ringwald 2504930cef6SMatthias Ringwald /* --- Cleanup --- */ 2514930cef6SMatthias Ringwald 2524930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2534930cef6SMatthias Ringwald free(enc[ich]); 2544930cef6SMatthias Ringwald 2554930cef6SMatthias Ringwald if (fp_in != stdin) 2564930cef6SMatthias Ringwald fclose(fp_in); 2574930cef6SMatthias Ringwald 2584930cef6SMatthias Ringwald if (fp_out != stdout) 2594930cef6SMatthias Ringwald fclose(fp_out); 2604930cef6SMatthias Ringwald } 261