xref: /btstack/3rd-party/lc3-google/tools/dlc3.c (revision 4930cef6e21e6da2d7571b9259c7f0fb8bed3d01)
1*4930cef6SMatthias Ringwald /******************************************************************************
2*4930cef6SMatthias Ringwald  *
3*4930cef6SMatthias Ringwald  *  Copyright 2022 Google LLC
4*4930cef6SMatthias Ringwald  *
5*4930cef6SMatthias Ringwald  *  Licensed under the Apache License, Version 2.0 (the "License");
6*4930cef6SMatthias Ringwald  *  you may not use this file except in compliance with the License.
7*4930cef6SMatthias Ringwald  *  You may obtain a copy of the License at:
8*4930cef6SMatthias Ringwald  *
9*4930cef6SMatthias Ringwald  *  http://www.apache.org/licenses/LICENSE-2.0
10*4930cef6SMatthias Ringwald  *
11*4930cef6SMatthias Ringwald  *  Unless required by applicable law or agreed to in writing, software
12*4930cef6SMatthias Ringwald  *  distributed under the License is distributed on an "AS IS" BASIS,
13*4930cef6SMatthias Ringwald  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*4930cef6SMatthias Ringwald  *  See the License for the specific language governing permissions and
15*4930cef6SMatthias Ringwald  *  limitations under the License.
16*4930cef6SMatthias Ringwald  *
17*4930cef6SMatthias Ringwald  ******************************************************************************/
18*4930cef6SMatthias Ringwald 
19*4930cef6SMatthias Ringwald #define _POSIX_C_SOURCE 199309L
20*4930cef6SMatthias Ringwald 
21*4930cef6SMatthias Ringwald #include <stdalign.h>
22*4930cef6SMatthias Ringwald #include <stdio.h>
23*4930cef6SMatthias Ringwald #include <stdarg.h>
24*4930cef6SMatthias Ringwald #include <stdlib.h>
25*4930cef6SMatthias Ringwald #include <string.h>
26*4930cef6SMatthias Ringwald #include <math.h>
27*4930cef6SMatthias Ringwald #include <time.h>
28*4930cef6SMatthias Ringwald #include <errno.h>
29*4930cef6SMatthias Ringwald 
30*4930cef6SMatthias Ringwald #include <lc3.h>
31*4930cef6SMatthias Ringwald #include "lc3bin.h"
32*4930cef6SMatthias Ringwald #include "wave.h"
33*4930cef6SMatthias Ringwald 
34*4930cef6SMatthias Ringwald #ifndef MIN
35*4930cef6SMatthias Ringwald #define MIN(a, b)  ( (a) < (b) ? (a) : (b) )
36*4930cef6SMatthias Ringwald #endif
37*4930cef6SMatthias Ringwald 
38*4930cef6SMatthias Ringwald #ifndef MAX
39*4930cef6SMatthias Ringwald #define MAX(a, b)  ( (a) > (b) ? (a) : (b) )
40*4930cef6SMatthias Ringwald #endif
41*4930cef6SMatthias Ringwald 
42*4930cef6SMatthias Ringwald 
43*4930cef6SMatthias Ringwald /**
44*4930cef6SMatthias Ringwald  * Error handling
45*4930cef6SMatthias Ringwald  */
46*4930cef6SMatthias Ringwald 
47*4930cef6SMatthias Ringwald static void error(int status, const char *format, ...)
48*4930cef6SMatthias Ringwald {
49*4930cef6SMatthias Ringwald     va_list args;
50*4930cef6SMatthias Ringwald 
51*4930cef6SMatthias Ringwald     fflush(stdout);
52*4930cef6SMatthias Ringwald 
53*4930cef6SMatthias Ringwald     va_start(args, format);
54*4930cef6SMatthias Ringwald     vfprintf(stderr, format, args);
55*4930cef6SMatthias Ringwald     va_end(args);
56*4930cef6SMatthias Ringwald 
57*4930cef6SMatthias Ringwald     fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
58*4930cef6SMatthias Ringwald     exit(status);
59*4930cef6SMatthias Ringwald }
60*4930cef6SMatthias Ringwald 
61*4930cef6SMatthias Ringwald 
62*4930cef6SMatthias Ringwald /**
63*4930cef6SMatthias Ringwald  * Parameters
64*4930cef6SMatthias Ringwald  */
65*4930cef6SMatthias Ringwald 
66*4930cef6SMatthias Ringwald struct parameters {
67*4930cef6SMatthias Ringwald     const char *fname_in;
68*4930cef6SMatthias Ringwald     const char *fname_out;
69*4930cef6SMatthias Ringwald     int bitdepth;
70*4930cef6SMatthias Ringwald     int srate_hz;
71*4930cef6SMatthias Ringwald };
72*4930cef6SMatthias Ringwald 
73*4930cef6SMatthias Ringwald static struct parameters parse_args(int argc, char *argv[])
74*4930cef6SMatthias Ringwald {
75*4930cef6SMatthias Ringwald     static const char *usage =
76*4930cef6SMatthias Ringwald         "Usage: %s [in_file] [wav_file]\n"
77*4930cef6SMatthias Ringwald         "\n"
78*4930cef6SMatthias Ringwald         "wav_file\t"  "Input wave file, stdin if omitted\n"
79*4930cef6SMatthias Ringwald         "out_file\t"  "Output bitstream file, stdout if omitted\n"
80*4930cef6SMatthias Ringwald         "\n"
81*4930cef6SMatthias Ringwald         "Options:\n"
82*4930cef6SMatthias Ringwald         "\t-h\t"     "Display help\n"
83*4930cef6SMatthias Ringwald         "\t-b\t"     "Output bitdepth, 16 bits (default) or 24 bits\n"
84*4930cef6SMatthias Ringwald         "\t-r\t"     "Output samplerate, default is LC3 stream samplerate\n"
85*4930cef6SMatthias Ringwald         "\n";
86*4930cef6SMatthias Ringwald 
87*4930cef6SMatthias Ringwald     struct parameters p = { .bitdepth = 16 };
88*4930cef6SMatthias Ringwald 
89*4930cef6SMatthias Ringwald     for (int iarg = 1; iarg < argc; ) {
90*4930cef6SMatthias Ringwald         const char *arg = argv[iarg++];
91*4930cef6SMatthias Ringwald 
92*4930cef6SMatthias Ringwald         if (arg[0] == '-') {
93*4930cef6SMatthias Ringwald             if (arg[2] != '\0')
94*4930cef6SMatthias Ringwald                 error(EINVAL, "Option %s", arg);
95*4930cef6SMatthias Ringwald 
96*4930cef6SMatthias Ringwald             char opt = arg[1];
97*4930cef6SMatthias Ringwald             const char *optarg;
98*4930cef6SMatthias Ringwald 
99*4930cef6SMatthias Ringwald             switch (opt) {
100*4930cef6SMatthias Ringwald                 case 'b': case 'r':
101*4930cef6SMatthias Ringwald                     if (iarg >= argc)
102*4930cef6SMatthias Ringwald                         error(EINVAL, "Argument %s", arg);
103*4930cef6SMatthias Ringwald                     optarg = argv[iarg++];
104*4930cef6SMatthias Ringwald             }
105*4930cef6SMatthias Ringwald 
106*4930cef6SMatthias Ringwald             switch (opt) {
107*4930cef6SMatthias Ringwald                 case 'h': fprintf(stderr, usage, argv[0]); exit(0);
108*4930cef6SMatthias Ringwald                 case 'b': p.bitdepth = atoi(optarg); break;
109*4930cef6SMatthias Ringwald                 case 'r': p.srate_hz = atoi(optarg); break;
110*4930cef6SMatthias Ringwald                 default:
111*4930cef6SMatthias Ringwald                     error(EINVAL, "Option %s", arg);
112*4930cef6SMatthias Ringwald             }
113*4930cef6SMatthias Ringwald 
114*4930cef6SMatthias Ringwald         } else {
115*4930cef6SMatthias Ringwald 
116*4930cef6SMatthias Ringwald             if (!p.fname_in)
117*4930cef6SMatthias Ringwald                 p.fname_in = arg;
118*4930cef6SMatthias Ringwald             else if (!p.fname_out)
119*4930cef6SMatthias Ringwald                 p.fname_out = arg;
120*4930cef6SMatthias Ringwald             else
121*4930cef6SMatthias Ringwald                 error(EINVAL, "Argument %s", arg);
122*4930cef6SMatthias Ringwald         }
123*4930cef6SMatthias Ringwald     }
124*4930cef6SMatthias Ringwald 
125*4930cef6SMatthias Ringwald     return p;
126*4930cef6SMatthias Ringwald }
127*4930cef6SMatthias Ringwald 
128*4930cef6SMatthias Ringwald /**
129*4930cef6SMatthias Ringwald  * Return time in (us) from unspecified point in the past
130*4930cef6SMatthias Ringwald  */
131*4930cef6SMatthias Ringwald static unsigned clock_us(void)
132*4930cef6SMatthias Ringwald {
133*4930cef6SMatthias Ringwald     struct timespec ts;
134*4930cef6SMatthias Ringwald 
135*4930cef6SMatthias Ringwald     clock_gettime(CLOCK_REALTIME, &ts);
136*4930cef6SMatthias Ringwald 
137*4930cef6SMatthias Ringwald     return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
138*4930cef6SMatthias Ringwald }
139*4930cef6SMatthias Ringwald 
140*4930cef6SMatthias Ringwald /**
141*4930cef6SMatthias Ringwald  * Entry point
142*4930cef6SMatthias Ringwald  */
143*4930cef6SMatthias Ringwald int main(int argc, char *argv[])
144*4930cef6SMatthias Ringwald {
145*4930cef6SMatthias Ringwald     /* --- Read parameters --- */
146*4930cef6SMatthias Ringwald 
147*4930cef6SMatthias Ringwald     struct parameters p = parse_args(argc, argv);
148*4930cef6SMatthias Ringwald     FILE *fp_in = stdin, *fp_out = stdout;
149*4930cef6SMatthias Ringwald 
150*4930cef6SMatthias Ringwald     if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
151*4930cef6SMatthias Ringwald         error(errno, "%s", p.fname_in);
152*4930cef6SMatthias Ringwald 
153*4930cef6SMatthias Ringwald     if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
154*4930cef6SMatthias Ringwald         error(errno, "%s", p.fname_out);
155*4930cef6SMatthias Ringwald 
156*4930cef6SMatthias Ringwald     if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz))
157*4930cef6SMatthias Ringwald         error(EINVAL, "Samplerate %d Hz", p.srate_hz);
158*4930cef6SMatthias Ringwald 
159*4930cef6SMatthias Ringwald     if (p.bitdepth && p.bitdepth != 16 && p.bitdepth != 24)
160*4930cef6SMatthias Ringwald         error(EINVAL, "Bitdepth %d", p.bitdepth);
161*4930cef6SMatthias Ringwald 
162*4930cef6SMatthias Ringwald     /* --- Check parameters --- */
163*4930cef6SMatthias Ringwald 
164*4930cef6SMatthias Ringwald     int frame_us, srate_hz, nch, nsamples;
165*4930cef6SMatthias Ringwald 
166*4930cef6SMatthias Ringwald     if (lc3bin_read_header(fp_in, &frame_us, &srate_hz, &nch, &nsamples) < 0)
167*4930cef6SMatthias Ringwald         error(EINVAL, "LC3 binary input file");
168*4930cef6SMatthias Ringwald 
169*4930cef6SMatthias Ringwald     if (nch  < 1 || nch  > 2)
170*4930cef6SMatthias Ringwald         error(EINVAL, "Number of channels %d", nch);
171*4930cef6SMatthias Ringwald 
172*4930cef6SMatthias Ringwald     if (!LC3_CHECK_DT_US(frame_us))
173*4930cef6SMatthias Ringwald         error(EINVAL, "Frame duration");
174*4930cef6SMatthias Ringwald 
175*4930cef6SMatthias Ringwald     if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz < srate_hz))
176*4930cef6SMatthias Ringwald          error(EINVAL, "Samplerate %d Hz", srate_hz);
177*4930cef6SMatthias Ringwald 
178*4930cef6SMatthias Ringwald     int pcm_sbits = p.bitdepth;
179*4930cef6SMatthias Ringwald     int pcm_sbytes = 2 + 2*(pcm_sbits > 16);
180*4930cef6SMatthias Ringwald 
181*4930cef6SMatthias Ringwald     int pcm_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
182*4930cef6SMatthias Ringwald     int pcm_samples = !p.srate_hz ? nsamples :
183*4930cef6SMatthias Ringwald         ((int64_t)nsamples * pcm_srate_hz) / srate_hz;
184*4930cef6SMatthias Ringwald 
185*4930cef6SMatthias Ringwald     wave_write_header(fp_out,
186*4930cef6SMatthias Ringwald           pcm_sbits, pcm_sbytes, pcm_srate_hz, nch, pcm_samples);
187*4930cef6SMatthias Ringwald 
188*4930cef6SMatthias Ringwald     /* --- Setup decoding --- */
189*4930cef6SMatthias Ringwald 
190*4930cef6SMatthias Ringwald     int frame_samples = lc3_frame_samples(frame_us, pcm_srate_hz);
191*4930cef6SMatthias Ringwald     int encode_samples = pcm_samples +
192*4930cef6SMatthias Ringwald         lc3_delay_samples(frame_us, pcm_srate_hz);
193*4930cef6SMatthias Ringwald 
194*4930cef6SMatthias Ringwald     lc3_decoder_t dec[nch];
195*4930cef6SMatthias Ringwald     uint8_t in[nch * LC3_MAX_FRAME_BYTES];
196*4930cef6SMatthias Ringwald     int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes];
197*4930cef6SMatthias Ringwald     enum lc3_pcm_format pcm_fmt =
198*4930cef6SMatthias Ringwald         pcm_sbits == 24 ? LC3_PCM_FORMAT_S24 : LC3_PCM_FORMAT_S16;
199*4930cef6SMatthias Ringwald 
200*4930cef6SMatthias Ringwald     for (int ich = 0; ich < nch; ich++)
201*4930cef6SMatthias Ringwald         dec[ich] = lc3_setup_decoder(frame_us, srate_hz, p.srate_hz,
202*4930cef6SMatthias Ringwald             malloc(lc3_decoder_size(frame_us, pcm_srate_hz)));
203*4930cef6SMatthias Ringwald 
204*4930cef6SMatthias Ringwald     /* --- Decoding loop --- */
205*4930cef6SMatthias Ringwald 
206*4930cef6SMatthias Ringwald     static const char *dash_line = "========================================";
207*4930cef6SMatthias Ringwald 
208*4930cef6SMatthias Ringwald     int nsec = 0;
209*4930cef6SMatthias Ringwald     unsigned t0 = clock_us();
210*4930cef6SMatthias Ringwald 
211*4930cef6SMatthias Ringwald     for (int i = 0; i * frame_samples < encode_samples; i++) {
212*4930cef6SMatthias Ringwald 
213*4930cef6SMatthias Ringwald         int frame_bytes = lc3bin_read_data(fp_in, nch, in);
214*4930cef6SMatthias Ringwald 
215*4930cef6SMatthias Ringwald         if (floorf(i * frame_us * 1e-6) > nsec) {
216*4930cef6SMatthias Ringwald 
217*4930cef6SMatthias Ringwald             float progress = fminf((float)i * frame_samples / pcm_samples, 1);
218*4930cef6SMatthias Ringwald 
219*4930cef6SMatthias Ringwald             fprintf(stderr, "%02d:%02d [%-40s]\r",
220*4930cef6SMatthias Ringwald                     nsec / 60, nsec % 60,
221*4930cef6SMatthias Ringwald                     dash_line + (int)floorf((1 - progress) * 40));
222*4930cef6SMatthias Ringwald 
223*4930cef6SMatthias Ringwald             nsec = rint(i * frame_us * 1e-6);
224*4930cef6SMatthias Ringwald         }
225*4930cef6SMatthias Ringwald 
226*4930cef6SMatthias Ringwald         if (frame_bytes <= 0)
227*4930cef6SMatthias Ringwald             memset(pcm, 0, nch * frame_samples * pcm_sbytes);
228*4930cef6SMatthias Ringwald         else
229*4930cef6SMatthias Ringwald             for (int ich = 0; ich < nch; ich++)
230*4930cef6SMatthias Ringwald                 lc3_decode(dec[ich],
231*4930cef6SMatthias Ringwald                     in + ich * frame_bytes, frame_bytes,
232*4930cef6SMatthias Ringwald                     pcm_fmt, pcm + ich * pcm_sbytes, nch);
233*4930cef6SMatthias Ringwald 
234*4930cef6SMatthias Ringwald         int pcm_offset = i > 0 ? 0 : encode_samples - pcm_samples;
235*4930cef6SMatthias Ringwald         int pcm_nwrite = MIN(frame_samples - pcm_offset,
236*4930cef6SMatthias Ringwald             encode_samples - i*frame_samples);
237*4930cef6SMatthias Ringwald 
238*4930cef6SMatthias Ringwald         wave_write_pcm(fp_out, pcm_sbytes, pcm, nch, pcm_offset, pcm_nwrite);
239*4930cef6SMatthias Ringwald     }
240*4930cef6SMatthias Ringwald 
241*4930cef6SMatthias Ringwald     unsigned t = (clock_us() - t0) / 1000;
242*4930cef6SMatthias Ringwald     nsec = nsamples / srate_hz;
243*4930cef6SMatthias Ringwald 
244*4930cef6SMatthias Ringwald     fprintf(stderr, "%02d:%02d Decoded in %d.%03d seconds %20s\n",
245*4930cef6SMatthias Ringwald         nsec / 60, nsec % 60, t / 1000, t % 1000, "");
246*4930cef6SMatthias Ringwald 
247*4930cef6SMatthias Ringwald     /* --- Cleanup --- */
248*4930cef6SMatthias Ringwald 
249*4930cef6SMatthias Ringwald     for (int ich = 0; ich < nch; ich++)
250*4930cef6SMatthias Ringwald         free(dec[ich]);
251*4930cef6SMatthias Ringwald 
252*4930cef6SMatthias Ringwald     if (fp_in != stdin)
253*4930cef6SMatthias Ringwald         fclose(fp_in);
254*4930cef6SMatthias Ringwald 
255*4930cef6SMatthias Ringwald     if (fp_out != stdout)
256*4930cef6SMatthias Ringwald         fclose(fp_out);
257*4930cef6SMatthias Ringwald }
258