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