xref: /btstack/3rd-party/lc3-google/tools/dlc3.c (revision 6897da5c53aac5b1f90f41b5b15d0bd43d61dfff)
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 
34*6897da5cSDirk Helbig #define MAX_CHANNELS 2
35*6897da5cSDirk Helbig 
364930cef6SMatthias Ringwald #ifndef MIN
374930cef6SMatthias Ringwald #define MIN(a, b)  ( (a) < (b) ? (a) : (b) )
384930cef6SMatthias Ringwald #endif
394930cef6SMatthias Ringwald 
404930cef6SMatthias Ringwald #ifndef MAX
414930cef6SMatthias Ringwald #define MAX(a, b)  ( (a) > (b) ? (a) : (b) )
424930cef6SMatthias Ringwald #endif
434930cef6SMatthias Ringwald 
444930cef6SMatthias Ringwald 
454930cef6SMatthias Ringwald /**
464930cef6SMatthias Ringwald  * Error handling
474930cef6SMatthias Ringwald  */
484930cef6SMatthias Ringwald 
error(int status,const char * format,...)494930cef6SMatthias Ringwald static void error(int status, const char *format, ...)
504930cef6SMatthias Ringwald {
514930cef6SMatthias Ringwald     va_list args;
524930cef6SMatthias Ringwald 
534930cef6SMatthias Ringwald     fflush(stdout);
544930cef6SMatthias Ringwald 
554930cef6SMatthias Ringwald     va_start(args, format);
564930cef6SMatthias Ringwald     vfprintf(stderr, format, args);
574930cef6SMatthias Ringwald     va_end(args);
584930cef6SMatthias Ringwald 
594930cef6SMatthias Ringwald     fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
604930cef6SMatthias Ringwald     exit(status);
614930cef6SMatthias Ringwald }
624930cef6SMatthias Ringwald 
634930cef6SMatthias Ringwald 
644930cef6SMatthias Ringwald /**
654930cef6SMatthias Ringwald  * Parameters
664930cef6SMatthias Ringwald  */
674930cef6SMatthias Ringwald 
684930cef6SMatthias Ringwald struct parameters {
694930cef6SMatthias Ringwald     const char *fname_in;
704930cef6SMatthias Ringwald     const char *fname_out;
714930cef6SMatthias Ringwald     int bitdepth;
724930cef6SMatthias Ringwald     int srate_hz;
734930cef6SMatthias Ringwald };
744930cef6SMatthias Ringwald 
parse_args(int argc,char * argv[])754930cef6SMatthias Ringwald static struct parameters parse_args(int argc, char *argv[])
764930cef6SMatthias Ringwald {
774930cef6SMatthias Ringwald     static const char *usage =
78*6897da5cSDirk Helbig         "Usage: %s [wav_file] [out_file]\n"
794930cef6SMatthias Ringwald         "\n"
804930cef6SMatthias Ringwald         "wav_file\t"  "Input wave file, stdin if omitted\n"
814930cef6SMatthias Ringwald         "out_file\t"  "Output bitstream file, stdout if omitted\n"
824930cef6SMatthias Ringwald         "\n"
834930cef6SMatthias Ringwald         "Options:\n"
844930cef6SMatthias Ringwald         "\t-h\t"     "Display help\n"
854930cef6SMatthias Ringwald         "\t-b\t"     "Output bitdepth, 16 bits (default) or 24 bits\n"
864930cef6SMatthias Ringwald         "\t-r\t"     "Output samplerate, default is LC3 stream samplerate\n"
874930cef6SMatthias Ringwald         "\n";
884930cef6SMatthias Ringwald 
894930cef6SMatthias Ringwald     struct parameters p = { .bitdepth = 16 };
904930cef6SMatthias Ringwald 
914930cef6SMatthias Ringwald     for (int iarg = 1; iarg < argc; ) {
924930cef6SMatthias Ringwald         const char *arg = argv[iarg++];
934930cef6SMatthias Ringwald 
944930cef6SMatthias Ringwald         if (arg[0] == '-') {
954930cef6SMatthias Ringwald             if (arg[2] != '\0')
964930cef6SMatthias Ringwald                 error(EINVAL, "Option %s", arg);
974930cef6SMatthias Ringwald 
984930cef6SMatthias Ringwald             char opt = arg[1];
99*6897da5cSDirk Helbig             const char *optarg = NULL;
1004930cef6SMatthias Ringwald 
1014930cef6SMatthias Ringwald             switch (opt) {
1024930cef6SMatthias Ringwald                 case 'b': case 'r':
1034930cef6SMatthias Ringwald                     if (iarg >= argc)
1044930cef6SMatthias Ringwald                         error(EINVAL, "Argument %s", arg);
1054930cef6SMatthias Ringwald                     optarg = argv[iarg++];
1064930cef6SMatthias Ringwald             }
1074930cef6SMatthias Ringwald 
1084930cef6SMatthias Ringwald             switch (opt) {
1094930cef6SMatthias Ringwald                 case 'h': fprintf(stderr, usage, argv[0]); exit(0);
1104930cef6SMatthias Ringwald                 case 'b': p.bitdepth = atoi(optarg); break;
1114930cef6SMatthias Ringwald                 case 'r': p.srate_hz = atoi(optarg); break;
1124930cef6SMatthias Ringwald                 default:
1134930cef6SMatthias Ringwald                     error(EINVAL, "Option %s", arg);
1144930cef6SMatthias Ringwald             }
1154930cef6SMatthias Ringwald 
1164930cef6SMatthias Ringwald         } else {
1174930cef6SMatthias Ringwald 
1184930cef6SMatthias Ringwald             if (!p.fname_in)
1194930cef6SMatthias Ringwald                 p.fname_in = arg;
1204930cef6SMatthias Ringwald             else if (!p.fname_out)
1214930cef6SMatthias Ringwald                 p.fname_out = arg;
1224930cef6SMatthias Ringwald             else
1234930cef6SMatthias Ringwald                 error(EINVAL, "Argument %s", arg);
1244930cef6SMatthias Ringwald         }
1254930cef6SMatthias Ringwald     }
1264930cef6SMatthias Ringwald 
1274930cef6SMatthias Ringwald     return p;
1284930cef6SMatthias Ringwald }
1294930cef6SMatthias Ringwald 
1304930cef6SMatthias Ringwald /**
1314930cef6SMatthias Ringwald  * Return time in (us) from unspecified point in the past
1324930cef6SMatthias Ringwald  */
clock_us(void)1334930cef6SMatthias Ringwald static unsigned clock_us(void)
1344930cef6SMatthias Ringwald {
1354930cef6SMatthias Ringwald     struct timespec ts;
1364930cef6SMatthias Ringwald 
1374930cef6SMatthias Ringwald     clock_gettime(CLOCK_REALTIME, &ts);
1384930cef6SMatthias Ringwald 
1394930cef6SMatthias Ringwald     return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
1404930cef6SMatthias Ringwald }
1414930cef6SMatthias Ringwald 
1424930cef6SMatthias Ringwald /**
1434930cef6SMatthias Ringwald  * Entry point
1444930cef6SMatthias Ringwald  */
main(int argc,char * argv[])1454930cef6SMatthias Ringwald int main(int argc, char *argv[])
1464930cef6SMatthias Ringwald {
1474930cef6SMatthias Ringwald     /* --- Read parameters --- */
1484930cef6SMatthias Ringwald 
1494930cef6SMatthias Ringwald     struct parameters p = parse_args(argc, argv);
1504930cef6SMatthias Ringwald     FILE *fp_in = stdin, *fp_out = stdout;
1514930cef6SMatthias Ringwald 
1524930cef6SMatthias Ringwald     if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
1534930cef6SMatthias Ringwald         error(errno, "%s", p.fname_in);
1544930cef6SMatthias Ringwald 
1554930cef6SMatthias Ringwald     if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
1564930cef6SMatthias Ringwald         error(errno, "%s", p.fname_out);
1574930cef6SMatthias Ringwald 
1584930cef6SMatthias Ringwald     if (p.bitdepth && p.bitdepth != 16 && p.bitdepth != 24)
1594930cef6SMatthias Ringwald         error(EINVAL, "Bitdepth %d", p.bitdepth);
1604930cef6SMatthias Ringwald 
1614930cef6SMatthias Ringwald     /* --- Check parameters --- */
1624930cef6SMatthias Ringwald 
163*6897da5cSDirk Helbig     int frame_us, srate_hz, nchannels, nsamples;
164*6897da5cSDirk Helbig     bool hrmode;
1654930cef6SMatthias Ringwald 
166*6897da5cSDirk Helbig     if (lc3bin_read_header(fp_in,
167*6897da5cSDirk Helbig             &frame_us, &srate_hz, &hrmode, &nchannels, &nsamples) < 0)
1684930cef6SMatthias Ringwald         error(EINVAL, "LC3 binary input file");
1694930cef6SMatthias Ringwald 
170*6897da5cSDirk Helbig     if (nchannels < 1 || nchannels > MAX_CHANNELS)
171*6897da5cSDirk Helbig         error(EINVAL, "Number of channels %d", nchannels);
1724930cef6SMatthias Ringwald 
1734930cef6SMatthias Ringwald     if (!LC3_CHECK_DT_US(frame_us))
1744930cef6SMatthias Ringwald         error(EINVAL, "Frame duration");
1754930cef6SMatthias Ringwald 
176*6897da5cSDirk Helbig     if (!LC3_HR_CHECK_SR_HZ(hrmode, srate_hz))
1774930cef6SMatthias Ringwald          error(EINVAL, "Samplerate %d Hz", srate_hz);
1784930cef6SMatthias Ringwald 
179*6897da5cSDirk Helbig     if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(hrmode, p.srate_hz) ||
180*6897da5cSDirk Helbig                        p.srate_hz < srate_hz                     ))
181*6897da5cSDirk Helbig          error(EINVAL, "Output samplerate %d Hz", p.srate_hz);
182*6897da5cSDirk Helbig 
1834930cef6SMatthias Ringwald     int pcm_sbits = p.bitdepth;
1844c4eb519SMatthias Ringwald     int pcm_sbytes = pcm_sbits / 8;
1854930cef6SMatthias Ringwald 
1864930cef6SMatthias Ringwald     int pcm_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
1874930cef6SMatthias Ringwald     int pcm_samples = !p.srate_hz ? nsamples :
1884930cef6SMatthias Ringwald         ((int64_t)nsamples * pcm_srate_hz) / srate_hz;
1894930cef6SMatthias Ringwald 
1904930cef6SMatthias Ringwald     wave_write_header(fp_out,
191*6897da5cSDirk Helbig           pcm_sbits, pcm_sbytes, pcm_srate_hz, nchannels, pcm_samples);
1924930cef6SMatthias Ringwald 
1934930cef6SMatthias Ringwald     /* --- Setup decoding --- */
1944930cef6SMatthias Ringwald 
195*6897da5cSDirk Helbig     uint8_t in[2 * LC3_HR_MAX_FRAME_BYTES];
196*6897da5cSDirk Helbig     int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4];
197*6897da5cSDirk Helbig     lc3_decoder_t dec[2];
1984930cef6SMatthias Ringwald 
199*6897da5cSDirk Helbig     int frame_samples = lc3_hr_frame_samples(hrmode, frame_us, pcm_srate_hz);
200*6897da5cSDirk Helbig     int encode_samples = pcm_samples +
201*6897da5cSDirk Helbig         lc3_hr_delay_samples(hrmode, frame_us, pcm_srate_hz);
2024930cef6SMatthias Ringwald     enum lc3_pcm_format pcm_fmt =
2034c4eb519SMatthias Ringwald         pcm_sbits == 24 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16;
2044930cef6SMatthias Ringwald 
205*6897da5cSDirk Helbig     for (int ich = 0; ich < nchannels; ich++) {
206*6897da5cSDirk Helbig         dec[ich] = lc3_hr_setup_decoder(
207*6897da5cSDirk Helbig             hrmode, frame_us, srate_hz, p.srate_hz,
208*6897da5cSDirk Helbig             malloc(lc3_hr_decoder_size(hrmode, frame_us, pcm_srate_hz)));
209*6897da5cSDirk Helbig 
210*6897da5cSDirk Helbig         if (!dec[ich])
211*6897da5cSDirk Helbig             error(EINVAL, "Decoder initialization failed");
212*6897da5cSDirk Helbig     }
2134930cef6SMatthias Ringwald 
2144930cef6SMatthias Ringwald     /* --- Decoding loop --- */
2154930cef6SMatthias Ringwald 
2164930cef6SMatthias Ringwald     static const char *dash_line = "========================================";
2174930cef6SMatthias Ringwald 
2184930cef6SMatthias Ringwald     int nsec = 0;
219*6897da5cSDirk Helbig     int nerr = 0;
2204930cef6SMatthias Ringwald     unsigned t0 = clock_us();
2214930cef6SMatthias Ringwald 
2224930cef6SMatthias Ringwald     for (int i = 0; i * frame_samples < encode_samples; i++) {
2234930cef6SMatthias Ringwald 
224*6897da5cSDirk Helbig         int block_bytes = lc3bin_read_data(fp_in, nchannels, in);
2254930cef6SMatthias Ringwald 
2264930cef6SMatthias Ringwald         if (floorf(i * frame_us * 1e-6) > nsec) {
2274930cef6SMatthias Ringwald 
2284930cef6SMatthias Ringwald             float progress = fminf((float)i * frame_samples / pcm_samples, 1);
2294930cef6SMatthias Ringwald 
2304930cef6SMatthias Ringwald             fprintf(stderr, "%02d:%02d [%-40s]\r",
2314930cef6SMatthias Ringwald                     nsec / 60, nsec % 60,
2324930cef6SMatthias Ringwald                     dash_line + (int)floorf((1 - progress) * 40));
2334930cef6SMatthias Ringwald 
2344930cef6SMatthias Ringwald             nsec = rint(i * frame_us * 1e-6);
2354930cef6SMatthias Ringwald         }
2364930cef6SMatthias Ringwald 
237*6897da5cSDirk Helbig         if (block_bytes <= 0)
238*6897da5cSDirk Helbig             memset(pcm, 0, nchannels * frame_samples * pcm_sbytes);
239*6897da5cSDirk Helbig         else {
240*6897da5cSDirk Helbig             const uint8_t *in_ptr = in;
241*6897da5cSDirk Helbig             for (int ich = 0; ich < nchannels; ich++) {
242*6897da5cSDirk Helbig                 int frame_bytes = block_bytes / nchannels
243*6897da5cSDirk Helbig                     + (ich < block_bytes % nchannels);
244*6897da5cSDirk Helbig 
245*6897da5cSDirk Helbig                 int res = lc3_decode(dec[ich], in_ptr, frame_bytes,
246*6897da5cSDirk Helbig                     pcm_fmt, pcm + ich * pcm_sbytes, nchannels);
247*6897da5cSDirk Helbig 
248*6897da5cSDirk Helbig                 nerr += (res != 0);
249*6897da5cSDirk Helbig                 in_ptr += frame_bytes;
250*6897da5cSDirk Helbig             }
251*6897da5cSDirk Helbig         }
2524930cef6SMatthias Ringwald 
2534930cef6SMatthias Ringwald         int pcm_offset = i > 0 ? 0 : encode_samples - pcm_samples;
2544930cef6SMatthias Ringwald         int pcm_nwrite = MIN(frame_samples - pcm_offset,
2554930cef6SMatthias Ringwald             encode_samples - i*frame_samples);
2564930cef6SMatthias Ringwald 
257*6897da5cSDirk Helbig         wave_write_pcm(fp_out,
258*6897da5cSDirk Helbig             pcm_sbytes, pcm, nchannels, pcm_offset, pcm_nwrite);
2594930cef6SMatthias Ringwald     }
2604930cef6SMatthias Ringwald 
2614930cef6SMatthias Ringwald     unsigned t = (clock_us() - t0) / 1000;
2624930cef6SMatthias Ringwald     nsec = nsamples / srate_hz;
2634930cef6SMatthias Ringwald 
2644930cef6SMatthias Ringwald     fprintf(stderr, "%02d:%02d Decoded in %d.%03d seconds %20s\n",
2654930cef6SMatthias Ringwald         nsec / 60, nsec % 60, t / 1000, t % 1000, "");
2664930cef6SMatthias Ringwald 
267*6897da5cSDirk Helbig     if (nerr)
268*6897da5cSDirk Helbig         fprintf(stderr, "Warning: Decoding of %d frames failed!\n", nerr);
269*6897da5cSDirk Helbig 
2704930cef6SMatthias Ringwald     /* --- Cleanup --- */
2714930cef6SMatthias Ringwald 
272*6897da5cSDirk Helbig     for (int ich = 0; ich < nchannels; ich++)
2734930cef6SMatthias Ringwald         free(dec[ich]);
2744930cef6SMatthias Ringwald 
2754930cef6SMatthias Ringwald     if (fp_in != stdin)
2764930cef6SMatthias Ringwald         fclose(fp_in);
2774930cef6SMatthias Ringwald 
2784930cef6SMatthias Ringwald     if (fp_out != stdout)
2794930cef6SMatthias Ringwald         fclose(fp_out);
2804930cef6SMatthias Ringwald }
281