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 #ifndef MIN 354930cef6SMatthias Ringwald #define MIN(a, b) ( (a) < (b) ? (a) : (b) ) 364930cef6SMatthias Ringwald #endif 374930cef6SMatthias Ringwald 384930cef6SMatthias Ringwald #ifndef MAX 394930cef6SMatthias Ringwald #define MAX(a, b) ( (a) > (b) ? (a) : (b) ) 404930cef6SMatthias Ringwald #endif 414930cef6SMatthias Ringwald 424930cef6SMatthias Ringwald 434930cef6SMatthias Ringwald /** 444930cef6SMatthias Ringwald * Error handling 454930cef6SMatthias Ringwald */ 464930cef6SMatthias Ringwald 474930cef6SMatthias Ringwald static void error(int status, const char *format, ...) 484930cef6SMatthias Ringwald { 494930cef6SMatthias Ringwald va_list args; 504930cef6SMatthias Ringwald 514930cef6SMatthias Ringwald fflush(stdout); 524930cef6SMatthias Ringwald 534930cef6SMatthias Ringwald va_start(args, format); 544930cef6SMatthias Ringwald vfprintf(stderr, format, args); 554930cef6SMatthias Ringwald va_end(args); 564930cef6SMatthias Ringwald 574930cef6SMatthias Ringwald fprintf(stderr, status ? ": %s\n" : "\n", strerror(status)); 584930cef6SMatthias Ringwald exit(status); 594930cef6SMatthias Ringwald } 604930cef6SMatthias Ringwald 614930cef6SMatthias Ringwald 624930cef6SMatthias Ringwald /** 634930cef6SMatthias Ringwald * Parameters 644930cef6SMatthias Ringwald */ 654930cef6SMatthias Ringwald 664930cef6SMatthias Ringwald struct parameters { 674930cef6SMatthias Ringwald const char *fname_in; 684930cef6SMatthias Ringwald const char *fname_out; 694930cef6SMatthias Ringwald int bitdepth; 704930cef6SMatthias Ringwald int srate_hz; 714930cef6SMatthias Ringwald }; 724930cef6SMatthias Ringwald 734930cef6SMatthias Ringwald static struct parameters parse_args(int argc, char *argv[]) 744930cef6SMatthias Ringwald { 754930cef6SMatthias Ringwald static const char *usage = 764930cef6SMatthias Ringwald "Usage: %s [in_file] [wav_file]\n" 774930cef6SMatthias Ringwald "\n" 784930cef6SMatthias Ringwald "wav_file\t" "Input wave file, stdin if omitted\n" 794930cef6SMatthias Ringwald "out_file\t" "Output bitstream file, stdout if omitted\n" 804930cef6SMatthias Ringwald "\n" 814930cef6SMatthias Ringwald "Options:\n" 824930cef6SMatthias Ringwald "\t-h\t" "Display help\n" 834930cef6SMatthias Ringwald "\t-b\t" "Output bitdepth, 16 bits (default) or 24 bits\n" 844930cef6SMatthias Ringwald "\t-r\t" "Output samplerate, default is LC3 stream samplerate\n" 854930cef6SMatthias Ringwald "\n"; 864930cef6SMatthias Ringwald 874930cef6SMatthias Ringwald struct parameters p = { .bitdepth = 16 }; 884930cef6SMatthias Ringwald 894930cef6SMatthias Ringwald for (int iarg = 1; iarg < argc; ) { 904930cef6SMatthias Ringwald const char *arg = argv[iarg++]; 914930cef6SMatthias Ringwald 924930cef6SMatthias Ringwald if (arg[0] == '-') { 934930cef6SMatthias Ringwald if (arg[2] != '\0') 944930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg); 954930cef6SMatthias Ringwald 964930cef6SMatthias Ringwald char opt = arg[1]; 974930cef6SMatthias Ringwald const char *optarg; 984930cef6SMatthias Ringwald 994930cef6SMatthias Ringwald switch (opt) { 1004930cef6SMatthias Ringwald case 'b': case 'r': 1014930cef6SMatthias Ringwald if (iarg >= argc) 1024930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg); 1034930cef6SMatthias Ringwald optarg = argv[iarg++]; 1044930cef6SMatthias Ringwald } 1054930cef6SMatthias Ringwald 1064930cef6SMatthias Ringwald switch (opt) { 1074930cef6SMatthias Ringwald case 'h': fprintf(stderr, usage, argv[0]); exit(0); 1084930cef6SMatthias Ringwald case 'b': p.bitdepth = atoi(optarg); break; 1094930cef6SMatthias Ringwald case 'r': p.srate_hz = atoi(optarg); break; 1104930cef6SMatthias Ringwald default: 1114930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg); 1124930cef6SMatthias Ringwald } 1134930cef6SMatthias Ringwald 1144930cef6SMatthias Ringwald } else { 1154930cef6SMatthias Ringwald 1164930cef6SMatthias Ringwald if (!p.fname_in) 1174930cef6SMatthias Ringwald p.fname_in = arg; 1184930cef6SMatthias Ringwald else if (!p.fname_out) 1194930cef6SMatthias Ringwald p.fname_out = arg; 1204930cef6SMatthias Ringwald else 1214930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg); 1224930cef6SMatthias Ringwald } 1234930cef6SMatthias Ringwald } 1244930cef6SMatthias Ringwald 1254930cef6SMatthias Ringwald return p; 1264930cef6SMatthias Ringwald } 1274930cef6SMatthias Ringwald 1284930cef6SMatthias Ringwald /** 1294930cef6SMatthias Ringwald * Return time in (us) from unspecified point in the past 1304930cef6SMatthias Ringwald */ 1314930cef6SMatthias Ringwald static unsigned clock_us(void) 1324930cef6SMatthias Ringwald { 1334930cef6SMatthias Ringwald struct timespec ts; 1344930cef6SMatthias Ringwald 1354930cef6SMatthias Ringwald clock_gettime(CLOCK_REALTIME, &ts); 1364930cef6SMatthias Ringwald 1374930cef6SMatthias Ringwald return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000); 1384930cef6SMatthias Ringwald } 1394930cef6SMatthias Ringwald 1404930cef6SMatthias Ringwald /** 1414930cef6SMatthias Ringwald * Entry point 1424930cef6SMatthias Ringwald */ 1434930cef6SMatthias Ringwald int main(int argc, char *argv[]) 1444930cef6SMatthias Ringwald { 1454930cef6SMatthias Ringwald /* --- Read parameters --- */ 1464930cef6SMatthias Ringwald 1474930cef6SMatthias Ringwald struct parameters p = parse_args(argc, argv); 1484930cef6SMatthias Ringwald FILE *fp_in = stdin, *fp_out = stdout; 1494930cef6SMatthias Ringwald 1504930cef6SMatthias Ringwald if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL) 1514930cef6SMatthias Ringwald error(errno, "%s", p.fname_in); 1524930cef6SMatthias Ringwald 1534930cef6SMatthias Ringwald if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL) 1544930cef6SMatthias Ringwald error(errno, "%s", p.fname_out); 1554930cef6SMatthias Ringwald 1564930cef6SMatthias Ringwald if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz)) 1574930cef6SMatthias Ringwald error(EINVAL, "Samplerate %d Hz", p.srate_hz); 1584930cef6SMatthias Ringwald 1594930cef6SMatthias Ringwald if (p.bitdepth && p.bitdepth != 16 && p.bitdepth != 24) 1604930cef6SMatthias Ringwald error(EINVAL, "Bitdepth %d", p.bitdepth); 1614930cef6SMatthias Ringwald 1624930cef6SMatthias Ringwald /* --- Check parameters --- */ 1634930cef6SMatthias Ringwald 1644930cef6SMatthias Ringwald int frame_us, srate_hz, nch, nsamples; 1654930cef6SMatthias Ringwald 1664930cef6SMatthias Ringwald if (lc3bin_read_header(fp_in, &frame_us, &srate_hz, &nch, &nsamples) < 0) 1674930cef6SMatthias Ringwald error(EINVAL, "LC3 binary input file"); 1684930cef6SMatthias Ringwald 1694930cef6SMatthias Ringwald if (nch < 1 || nch > 2) 1704930cef6SMatthias Ringwald error(EINVAL, "Number of channels %d", nch); 1714930cef6SMatthias Ringwald 1724930cef6SMatthias Ringwald if (!LC3_CHECK_DT_US(frame_us)) 1734930cef6SMatthias Ringwald error(EINVAL, "Frame duration"); 1744930cef6SMatthias Ringwald 1754930cef6SMatthias Ringwald if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz < srate_hz)) 1764930cef6SMatthias Ringwald error(EINVAL, "Samplerate %d Hz", srate_hz); 1774930cef6SMatthias Ringwald 1784930cef6SMatthias Ringwald int pcm_sbits = p.bitdepth; 179*4c4eb519SMatthias Ringwald int pcm_sbytes = pcm_sbits / 8; 1804930cef6SMatthias Ringwald 1814930cef6SMatthias Ringwald int pcm_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz; 1824930cef6SMatthias Ringwald int pcm_samples = !p.srate_hz ? nsamples : 1834930cef6SMatthias Ringwald ((int64_t)nsamples * pcm_srate_hz) / srate_hz; 1844930cef6SMatthias Ringwald 1854930cef6SMatthias Ringwald wave_write_header(fp_out, 1864930cef6SMatthias Ringwald pcm_sbits, pcm_sbytes, pcm_srate_hz, nch, pcm_samples); 1874930cef6SMatthias Ringwald 1884930cef6SMatthias Ringwald /* --- Setup decoding --- */ 1894930cef6SMatthias Ringwald 1904930cef6SMatthias Ringwald int frame_samples = lc3_frame_samples(frame_us, pcm_srate_hz); 1914930cef6SMatthias Ringwald int encode_samples = pcm_samples + 1924930cef6SMatthias Ringwald lc3_delay_samples(frame_us, pcm_srate_hz); 1934930cef6SMatthias Ringwald 1944930cef6SMatthias Ringwald lc3_decoder_t dec[nch]; 1954930cef6SMatthias Ringwald uint8_t in[nch * LC3_MAX_FRAME_BYTES]; 1964930cef6SMatthias Ringwald int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes]; 1974930cef6SMatthias Ringwald enum lc3_pcm_format pcm_fmt = 198*4c4eb519SMatthias Ringwald pcm_sbits == 24 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16; 1994930cef6SMatthias Ringwald 2004930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2014930cef6SMatthias Ringwald dec[ich] = lc3_setup_decoder(frame_us, srate_hz, p.srate_hz, 2024930cef6SMatthias Ringwald malloc(lc3_decoder_size(frame_us, pcm_srate_hz))); 2034930cef6SMatthias Ringwald 2044930cef6SMatthias Ringwald /* --- Decoding loop --- */ 2054930cef6SMatthias Ringwald 2064930cef6SMatthias Ringwald static const char *dash_line = "========================================"; 2074930cef6SMatthias Ringwald 2084930cef6SMatthias Ringwald int nsec = 0; 2094930cef6SMatthias Ringwald unsigned t0 = clock_us(); 2104930cef6SMatthias Ringwald 2114930cef6SMatthias Ringwald for (int i = 0; i * frame_samples < encode_samples; i++) { 2124930cef6SMatthias Ringwald 2134930cef6SMatthias Ringwald int frame_bytes = lc3bin_read_data(fp_in, nch, in); 2144930cef6SMatthias Ringwald 2154930cef6SMatthias Ringwald if (floorf(i * frame_us * 1e-6) > nsec) { 2164930cef6SMatthias Ringwald 2174930cef6SMatthias Ringwald float progress = fminf((float)i * frame_samples / pcm_samples, 1); 2184930cef6SMatthias Ringwald 2194930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d [%-40s]\r", 2204930cef6SMatthias Ringwald nsec / 60, nsec % 60, 2214930cef6SMatthias Ringwald dash_line + (int)floorf((1 - progress) * 40)); 2224930cef6SMatthias Ringwald 2234930cef6SMatthias Ringwald nsec = rint(i * frame_us * 1e-6); 2244930cef6SMatthias Ringwald } 2254930cef6SMatthias Ringwald 2264930cef6SMatthias Ringwald if (frame_bytes <= 0) 2274930cef6SMatthias Ringwald memset(pcm, 0, nch * frame_samples * pcm_sbytes); 2284930cef6SMatthias Ringwald else 2294930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2304930cef6SMatthias Ringwald lc3_decode(dec[ich], 2314930cef6SMatthias Ringwald in + ich * frame_bytes, frame_bytes, 2324930cef6SMatthias Ringwald pcm_fmt, pcm + ich * pcm_sbytes, nch); 2334930cef6SMatthias Ringwald 2344930cef6SMatthias Ringwald int pcm_offset = i > 0 ? 0 : encode_samples - pcm_samples; 2354930cef6SMatthias Ringwald int pcm_nwrite = MIN(frame_samples - pcm_offset, 2364930cef6SMatthias Ringwald encode_samples - i*frame_samples); 2374930cef6SMatthias Ringwald 2384930cef6SMatthias Ringwald wave_write_pcm(fp_out, pcm_sbytes, pcm, nch, pcm_offset, pcm_nwrite); 2394930cef6SMatthias Ringwald } 2404930cef6SMatthias Ringwald 2414930cef6SMatthias Ringwald unsigned t = (clock_us() - t0) / 1000; 2424930cef6SMatthias Ringwald nsec = nsamples / srate_hz; 2434930cef6SMatthias Ringwald 2444930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d Decoded in %d.%03d seconds %20s\n", 2454930cef6SMatthias Ringwald nsec / 60, nsec % 60, t / 1000, t % 1000, ""); 2464930cef6SMatthias Ringwald 2474930cef6SMatthias Ringwald /* --- Cleanup --- */ 2484930cef6SMatthias Ringwald 2494930cef6SMatthias Ringwald for (int ich = 0; ich < nch; ich++) 2504930cef6SMatthias Ringwald free(dec[ich]); 2514930cef6SMatthias Ringwald 2524930cef6SMatthias Ringwald if (fp_in != stdin) 2534930cef6SMatthias Ringwald fclose(fp_in); 2544930cef6SMatthias Ringwald 2554930cef6SMatthias Ringwald if (fp_out != stdout) 2564930cef6SMatthias Ringwald fclose(fp_out); 2574930cef6SMatthias Ringwald } 258