xref: /btstack/3rd-party/lc3-google/tools/elc3.c (revision 4c4eb519208b4224604d94b3ed1931841ddd93bb)
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