1 /******************************************************************************
2  *
3  *  Copyright 2022 Google LLC
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #define _POSIX_C_SOURCE 199309L
20 
21 #include <stdalign.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <time.h>
28 #include <errno.h>
29 
30 #include <lc3.h>
31 #include "lc3bin.h"
32 #include "wave.h"
33 
34 #define MAX_CHANNELS 2
35 
36 
37 /**
38  * Error handling
39  */
40 
error(int status,const char * format,...)41 static void error(int status, const char *format, ...)
42 {
43     va_list args;
44 
45     fflush(stdout);
46 
47     va_start(args, format);
48     vfprintf(stderr, format, args);
49     va_end(args);
50 
51     fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
52     exit(status);
53 }
54 
55 
56 /**
57  * Parameters
58  */
59 
60 struct parameters {
61     const char *fname_in;
62     const char *fname_out;
63     float frame_ms;
64     int srate_hz;
65     bool hrmode;
66     int bitrate;
67 };
68 
parse_args(int argc,char * argv[])69 static struct parameters parse_args(int argc, char *argv[])
70 {
71     static const char *usage =
72         "Usage: %s [options] [wav_file] [out_file]\n"
73         "\n"
74         "wav_file\t"  "Input wave file, stdin if omitted\n"
75         "out_file\t"  "Output bitstream file, stdout if omitted\n"
76         "\n"
77         "Options:\n"
78         "\t-h\t"     "Display help\n"
79         "\t-b\t"     "Bitrate in bps (mandatory)\n"
80         "\t-m\t"     "Frame duration in ms (default 10)\n"
81         "\t-r\t"     "Encoder samplerate (default is input samplerate)\n"
82         "\t-H\t"     "Enable high-resolution mode\n"
83         "\n";
84 
85     struct parameters p = { .frame_ms = 10 };
86 
87     for (int iarg = 1; iarg < argc; ) {
88         const char *arg = argv[iarg++];
89 
90         if (arg[0] == '-') {
91             if (arg[2] != '\0')
92                 error(EINVAL, "Option %s", arg);
93 
94             char opt = arg[1];
95             const char *optarg = NULL;
96 
97             switch (opt) {
98                 case 'b': case 'm': case 'r':
99                     if (iarg >= argc)
100                         error(EINVAL, "Argument %s", arg);
101                     optarg = argv[iarg++];
102             }
103 
104             switch (opt) {
105                 case 'h': fprintf(stderr, usage, argv[0]); exit(0);
106                 case 'b': p.bitrate = atoi(optarg); break;
107                 case 'm': p.frame_ms = atof(optarg); break;
108                 case 'r': p.srate_hz = atoi(optarg); break;
109                 case 'H': p.hrmode = true; break;
110                 default:
111                     error(EINVAL, "Option %s", arg);
112             }
113 
114         } else {
115 
116             if (!p.fname_in)
117                 p.fname_in = arg;
118             else if (!p.fname_out)
119                 p.fname_out = arg;
120             else
121                 error(EINVAL, "Argument %s", arg);
122         }
123     }
124 
125     return p;
126 }
127 
128 
129 /**
130  * Return time in (us) from unspecified point in the past
131  */
132 
clock_us(void)133 static unsigned clock_us(void)
134 {
135     struct timespec ts;
136 
137     clock_gettime(CLOCK_MONOTONIC, &ts);
138 
139     return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
140 }
141 
142 
143 /**
144  * Entry point
145  */
146 
main(int argc,char * argv[])147 int main(int argc, char *argv[])
148 {
149     /* --- Read parameters --- */
150 
151     struct parameters p = parse_args(argc, argv);
152     FILE *fp_in = stdin, *fp_out = stdout;
153 
154     if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
155         error(errno, "%s", p.fname_in);
156 
157     if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
158         error(errno, "%s", p.fname_out);
159 
160     /* --- Check parameters --- */
161 
162     int frame_us = p.frame_ms * 1000;
163     int srate_hz, nchannels, nsamples;
164     int pcm_sbits, pcm_sbytes;
165 
166     if (wave_read_header(fp_in,
167             &pcm_sbits, &pcm_sbytes, &srate_hz, &nchannels, &nsamples) < 0)
168         error(EINVAL, "Bad or unsupported WAVE input file");
169 
170     if (p.bitrate <= 0)
171         error(EINVAL, "Bitrate");
172 
173     if (!LC3_CHECK_DT_US(frame_us))
174         error(EINVAL, "Frame duration");
175 
176     if (!LC3_HR_CHECK_SR_HZ(p.hrmode, srate_hz))
177         error(EINVAL, "Samplerate %d Hz", srate_hz);
178 
179     if (pcm_sbits != 16 && pcm_sbits != 24)
180         error(EINVAL, "Bitdepth %d", pcm_sbits);
181 
182     if ((pcm_sbits == 16 && pcm_sbytes != 16/8) ||
183         (pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8))
184         error(EINVAL, "Sample storage on %d bytes", pcm_sbytes);
185 
186     if (nchannels < 1 || nchannels > MAX_CHANNELS)
187         error(EINVAL, "Number of channels %d", nchannels);
188 
189     if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(p.hrmode, p.srate_hz) ||
190                        p.srate_hz > srate_hz                       ))
191         error(EINVAL, "Encoder samplerate %d Hz", p.srate_hz);
192 
193     int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
194     int enc_samples = !p.srate_hz ? nsamples :
195         ((int64_t)nsamples * enc_srate_hz) / srate_hz;
196 
197     int block_bytes = lc3_hr_frame_block_bytes(
198         p.hrmode, frame_us, srate_hz, nchannels, p.bitrate);
199 
200     int bitrate = lc3_hr_resolve_bitrate(
201         p.hrmode, frame_us, srate_hz, block_bytes);
202 
203     if (bitrate != p.bitrate)
204         fprintf(stderr, "Bitrate adjusted to %d bps\n", bitrate);
205 
206     lc3bin_write_header(fp_out,
207         frame_us, enc_srate_hz, p.hrmode,
208         bitrate, nchannels, enc_samples);
209 
210     /* --- Setup encoding --- */
211 
212     int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4];
213     uint8_t out[2 * LC3_HR_MAX_FRAME_BYTES];
214     lc3_encoder_t enc[2];
215 
216     int frame_samples = lc3_hr_frame_samples(
217         p.hrmode, frame_us, srate_hz);
218     int encode_samples = nsamples + lc3_hr_delay_samples(
219         p.hrmode, frame_us, srate_hz);
220     enum lc3_pcm_format pcm_fmt =
221         pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 :
222         pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16;
223 
224     for (int ich = 0; ich < nchannels; ich++) {
225         enc[ich] = lc3_hr_setup_encoder(
226             p.hrmode, frame_us, enc_srate_hz, srate_hz,
227             malloc(lc3_hr_encoder_size(p.hrmode, frame_us, srate_hz)));
228 
229         if (!enc[ich])
230             error(EINVAL, "Encoder initialization failed");
231     }
232 
233     /* --- Encoding loop --- */
234 
235     static const char *dash_line = "========================================";
236 
237     int nsec = 0;
238     unsigned t0 = clock_us();
239 
240     for (int i = 0; i * frame_samples < encode_samples; i++) {
241 
242         int nread = wave_read_pcm(fp_in, pcm_sbytes, nchannels, frame_samples, pcm);
243 
244         memset(pcm + nread * nchannels * pcm_sbytes, 0,
245             nchannels * (frame_samples - nread) * pcm_sbytes);
246 
247         if (floorf(i * frame_us * 1e-6) > nsec) {
248             float progress = fminf(
249                 (float)i * frame_samples / encode_samples, 1);
250 
251             fprintf(stderr, "%02d:%02d [%-40s]\r",
252                     nsec / 60, nsec % 60,
253                     dash_line + (int)floorf((1 - progress) * 40));
254 
255             nsec = (int)(i * frame_us * 1e-6);
256         }
257 
258         uint8_t *out_ptr = out;
259         for (int ich = 0; ich < nchannels; ich++) {
260             int frame_bytes = block_bytes / nchannels
261                 + (ich < block_bytes % nchannels);
262 
263             lc3_encode(enc[ich],
264                 pcm_fmt, pcm + ich * pcm_sbytes, nchannels,
265                 frame_bytes, out_ptr);
266 
267             out_ptr += frame_bytes;
268         }
269 
270         lc3bin_write_data(fp_out, out, block_bytes);
271     }
272 
273     unsigned t = (clock_us() - t0) / 1000;
274     nsec = encode_samples / srate_hz;
275 
276     fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n",
277         nsec / 60, nsec % 60, t / 1000, t % 1000, "");
278 
279     /* --- Cleanup --- */
280 
281     for (int ich = 0; ich < nchannels; ich++)
282         free(enc[ich]);
283 
284     if (fp_in != stdin)
285         fclose(fp_in);
286 
287     if (fp_out != stdout)
288         fclose(fp_out);
289 }
290