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
374930cef6SMatthias Ringwald /**
384930cef6SMatthias Ringwald * Error handling
394930cef6SMatthias Ringwald */
404930cef6SMatthias Ringwald
error(int status,const char * format,...)414930cef6SMatthias Ringwald static void error(int status, const char *format, ...)
424930cef6SMatthias Ringwald {
434930cef6SMatthias Ringwald va_list args;
444930cef6SMatthias Ringwald
454930cef6SMatthias Ringwald fflush(stdout);
464930cef6SMatthias Ringwald
474930cef6SMatthias Ringwald va_start(args, format);
484930cef6SMatthias Ringwald vfprintf(stderr, format, args);
494930cef6SMatthias Ringwald va_end(args);
504930cef6SMatthias Ringwald
514930cef6SMatthias Ringwald fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
524930cef6SMatthias Ringwald exit(status);
534930cef6SMatthias Ringwald }
544930cef6SMatthias Ringwald
554930cef6SMatthias Ringwald
564930cef6SMatthias Ringwald /**
574930cef6SMatthias Ringwald * Parameters
584930cef6SMatthias Ringwald */
594930cef6SMatthias Ringwald
604930cef6SMatthias Ringwald struct parameters {
614930cef6SMatthias Ringwald const char *fname_in;
624930cef6SMatthias Ringwald const char *fname_out;
634930cef6SMatthias Ringwald float frame_ms;
644930cef6SMatthias Ringwald int srate_hz;
65*6897da5cSDirk Helbig bool hrmode;
664930cef6SMatthias Ringwald int bitrate;
674930cef6SMatthias Ringwald };
684930cef6SMatthias Ringwald
parse_args(int argc,char * argv[])694930cef6SMatthias Ringwald static struct parameters parse_args(int argc, char *argv[])
704930cef6SMatthias Ringwald {
714930cef6SMatthias Ringwald static const char *usage =
724930cef6SMatthias Ringwald "Usage: %s [options] [wav_file] [out_file]\n"
734930cef6SMatthias Ringwald "\n"
744930cef6SMatthias Ringwald "wav_file\t" "Input wave file, stdin if omitted\n"
754930cef6SMatthias Ringwald "out_file\t" "Output bitstream file, stdout if omitted\n"
764930cef6SMatthias Ringwald "\n"
774930cef6SMatthias Ringwald "Options:\n"
784930cef6SMatthias Ringwald "\t-h\t" "Display help\n"
794930cef6SMatthias Ringwald "\t-b\t" "Bitrate in bps (mandatory)\n"
804930cef6SMatthias Ringwald "\t-m\t" "Frame duration in ms (default 10)\n"
814930cef6SMatthias Ringwald "\t-r\t" "Encoder samplerate (default is input samplerate)\n"
82*6897da5cSDirk Helbig "\t-H\t" "Enable high-resolution mode\n"
834930cef6SMatthias Ringwald "\n";
844930cef6SMatthias Ringwald
854930cef6SMatthias Ringwald struct parameters p = { .frame_ms = 10 };
864930cef6SMatthias Ringwald
874930cef6SMatthias Ringwald for (int iarg = 1; iarg < argc; ) {
884930cef6SMatthias Ringwald const char *arg = argv[iarg++];
894930cef6SMatthias Ringwald
904930cef6SMatthias Ringwald if (arg[0] == '-') {
914930cef6SMatthias Ringwald if (arg[2] != '\0')
924930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg);
934930cef6SMatthias Ringwald
944930cef6SMatthias Ringwald char opt = arg[1];
95*6897da5cSDirk Helbig const char *optarg = NULL;
964930cef6SMatthias Ringwald
974930cef6SMatthias Ringwald switch (opt) {
984930cef6SMatthias Ringwald case 'b': case 'm': case 'r':
994930cef6SMatthias Ringwald if (iarg >= argc)
1004930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg);
1014930cef6SMatthias Ringwald optarg = argv[iarg++];
1024930cef6SMatthias Ringwald }
1034930cef6SMatthias Ringwald
1044930cef6SMatthias Ringwald switch (opt) {
1054930cef6SMatthias Ringwald case 'h': fprintf(stderr, usage, argv[0]); exit(0);
1064930cef6SMatthias Ringwald case 'b': p.bitrate = atoi(optarg); break;
1074930cef6SMatthias Ringwald case 'm': p.frame_ms = atof(optarg); break;
1084930cef6SMatthias Ringwald case 'r': p.srate_hz = atoi(optarg); break;
109*6897da5cSDirk Helbig case 'H': p.hrmode = true; break;
1104930cef6SMatthias Ringwald default:
1114930cef6SMatthias Ringwald error(EINVAL, "Option %s", arg);
1124930cef6SMatthias Ringwald }
1134930cef6SMatthias Ringwald
1144930cef6SMatthias Ringwald } else {
1154930cef6SMatthias Ringwald
1164930cef6SMatthias Ringwald if (!p.fname_in)
1174930cef6SMatthias Ringwald p.fname_in = arg;
1184930cef6SMatthias Ringwald else if (!p.fname_out)
1194930cef6SMatthias Ringwald p.fname_out = arg;
1204930cef6SMatthias Ringwald else
1214930cef6SMatthias Ringwald error(EINVAL, "Argument %s", arg);
1224930cef6SMatthias Ringwald }
1234930cef6SMatthias Ringwald }
1244930cef6SMatthias Ringwald
1254930cef6SMatthias Ringwald return p;
1264930cef6SMatthias Ringwald }
1274930cef6SMatthias Ringwald
1284930cef6SMatthias Ringwald
1294930cef6SMatthias Ringwald /**
1304930cef6SMatthias Ringwald * Return time in (us) from unspecified point in the past
1314930cef6SMatthias Ringwald */
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_MONOTONIC, &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 /**
1444930cef6SMatthias Ringwald * Entry point
1454930cef6SMatthias Ringwald */
1464930cef6SMatthias Ringwald
main(int argc,char * argv[])1474930cef6SMatthias Ringwald int main(int argc, char *argv[])
1484930cef6SMatthias Ringwald {
1494930cef6SMatthias Ringwald /* --- Read parameters --- */
1504930cef6SMatthias Ringwald
1514930cef6SMatthias Ringwald struct parameters p = parse_args(argc, argv);
1524930cef6SMatthias Ringwald FILE *fp_in = stdin, *fp_out = stdout;
1534930cef6SMatthias Ringwald
1544930cef6SMatthias Ringwald if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
1554930cef6SMatthias Ringwald error(errno, "%s", p.fname_in);
1564930cef6SMatthias Ringwald
1574930cef6SMatthias Ringwald if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
1584930cef6SMatthias Ringwald error(errno, "%s", p.fname_out);
1594930cef6SMatthias Ringwald
1604930cef6SMatthias Ringwald /* --- Check parameters --- */
1614930cef6SMatthias Ringwald
1624930cef6SMatthias Ringwald int frame_us = p.frame_ms * 1000;
163*6897da5cSDirk Helbig int srate_hz, nchannels, nsamples;
1644930cef6SMatthias Ringwald int pcm_sbits, pcm_sbytes;
1654930cef6SMatthias Ringwald
1664930cef6SMatthias Ringwald if (wave_read_header(fp_in,
167*6897da5cSDirk Helbig &pcm_sbits, &pcm_sbytes, &srate_hz, &nchannels, &nsamples) < 0)
1684930cef6SMatthias Ringwald error(EINVAL, "Bad or unsupported WAVE input file");
1694930cef6SMatthias Ringwald
1704930cef6SMatthias Ringwald if (p.bitrate <= 0)
1714930cef6SMatthias Ringwald error(EINVAL, "Bitrate");
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(p.hrmode, srate_hz))
1774930cef6SMatthias Ringwald error(EINVAL, "Samplerate %d Hz", srate_hz);
1784930cef6SMatthias Ringwald
1794930cef6SMatthias Ringwald if (pcm_sbits != 16 && pcm_sbits != 24)
1804930cef6SMatthias Ringwald error(EINVAL, "Bitdepth %d", pcm_sbits);
1814930cef6SMatthias Ringwald
1824c4eb519SMatthias Ringwald if ((pcm_sbits == 16 && pcm_sbytes != 16/8) ||
1834c4eb519SMatthias Ringwald (pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8))
1844930cef6SMatthias Ringwald error(EINVAL, "Sample storage on %d bytes", pcm_sbytes);
1854930cef6SMatthias Ringwald
186*6897da5cSDirk Helbig if (nchannels < 1 || nchannels > MAX_CHANNELS)
187*6897da5cSDirk Helbig error(EINVAL, "Number of channels %d", nchannels);
188*6897da5cSDirk Helbig
189*6897da5cSDirk Helbig if (p.srate_hz && (!LC3_HR_CHECK_SR_HZ(p.hrmode, p.srate_hz) ||
190*6897da5cSDirk Helbig p.srate_hz > srate_hz ))
191*6897da5cSDirk Helbig error(EINVAL, "Encoder samplerate %d Hz", p.srate_hz);
1924930cef6SMatthias Ringwald
1934930cef6SMatthias Ringwald int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
1944930cef6SMatthias Ringwald int enc_samples = !p.srate_hz ? nsamples :
1954930cef6SMatthias Ringwald ((int64_t)nsamples * enc_srate_hz) / srate_hz;
1964930cef6SMatthias Ringwald
197*6897da5cSDirk Helbig int block_bytes = lc3_hr_frame_block_bytes(
198*6897da5cSDirk Helbig p.hrmode, frame_us, srate_hz, nchannels, p.bitrate);
199*6897da5cSDirk Helbig
200*6897da5cSDirk Helbig int bitrate = lc3_hr_resolve_bitrate(
201*6897da5cSDirk Helbig p.hrmode, frame_us, srate_hz, block_bytes);
202*6897da5cSDirk Helbig
203*6897da5cSDirk Helbig if (bitrate != p.bitrate)
204*6897da5cSDirk Helbig fprintf(stderr, "Bitrate adjusted to %d bps\n", bitrate);
205*6897da5cSDirk Helbig
2064930cef6SMatthias Ringwald lc3bin_write_header(fp_out,
207*6897da5cSDirk Helbig frame_us, enc_srate_hz, p.hrmode,
208*6897da5cSDirk Helbig bitrate, nchannels, enc_samples);
2094930cef6SMatthias Ringwald
2104930cef6SMatthias Ringwald /* --- Setup encoding --- */
2114930cef6SMatthias Ringwald
212*6897da5cSDirk Helbig int8_t alignas(int32_t) pcm[2 * LC3_HR_MAX_FRAME_SAMPLES*4];
213*6897da5cSDirk Helbig uint8_t out[2 * LC3_HR_MAX_FRAME_BYTES];
214*6897da5cSDirk Helbig lc3_encoder_t enc[2];
2154930cef6SMatthias Ringwald
216*6897da5cSDirk Helbig int frame_samples = lc3_hr_frame_samples(
217*6897da5cSDirk Helbig p.hrmode, frame_us, srate_hz);
218*6897da5cSDirk Helbig int encode_samples = nsamples + lc3_hr_delay_samples(
219*6897da5cSDirk Helbig p.hrmode, frame_us, srate_hz);
2204930cef6SMatthias Ringwald enum lc3_pcm_format pcm_fmt =
2214c4eb519SMatthias Ringwald pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 :
2224c4eb519SMatthias Ringwald pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16;
2234930cef6SMatthias Ringwald
224*6897da5cSDirk Helbig for (int ich = 0; ich < nchannels; ich++) {
225*6897da5cSDirk Helbig enc[ich] = lc3_hr_setup_encoder(
226*6897da5cSDirk Helbig p.hrmode, frame_us, enc_srate_hz, srate_hz,
227*6897da5cSDirk Helbig malloc(lc3_hr_encoder_size(p.hrmode, frame_us, srate_hz)));
228*6897da5cSDirk Helbig
229*6897da5cSDirk Helbig if (!enc[ich])
230*6897da5cSDirk Helbig error(EINVAL, "Encoder initialization failed");
231*6897da5cSDirk Helbig }
2324930cef6SMatthias Ringwald
2334930cef6SMatthias Ringwald /* --- Encoding loop --- */
2344930cef6SMatthias Ringwald
2354930cef6SMatthias Ringwald static const char *dash_line = "========================================";
2364930cef6SMatthias Ringwald
2374930cef6SMatthias Ringwald int nsec = 0;
2384930cef6SMatthias Ringwald unsigned t0 = clock_us();
2394930cef6SMatthias Ringwald
2404930cef6SMatthias Ringwald for (int i = 0; i * frame_samples < encode_samples; i++) {
2414930cef6SMatthias Ringwald
242*6897da5cSDirk Helbig int nread = wave_read_pcm(fp_in, pcm_sbytes, nchannels, frame_samples, pcm);
2434930cef6SMatthias Ringwald
244*6897da5cSDirk Helbig memset(pcm + nread * nchannels * pcm_sbytes, 0,
245*6897da5cSDirk Helbig nchannels * (frame_samples - nread) * pcm_sbytes);
2464930cef6SMatthias Ringwald
2474930cef6SMatthias Ringwald if (floorf(i * frame_us * 1e-6) > nsec) {
2484930cef6SMatthias Ringwald float progress = fminf(
2494930cef6SMatthias Ringwald (float)i * frame_samples / encode_samples, 1);
2504930cef6SMatthias Ringwald
2514930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d [%-40s]\r",
2524930cef6SMatthias Ringwald nsec / 60, nsec % 60,
2534930cef6SMatthias Ringwald dash_line + (int)floorf((1 - progress) * 40));
2544930cef6SMatthias Ringwald
2554930cef6SMatthias Ringwald nsec = (int)(i * frame_us * 1e-6);
2564930cef6SMatthias Ringwald }
2574930cef6SMatthias Ringwald
258*6897da5cSDirk Helbig uint8_t *out_ptr = out;
259*6897da5cSDirk Helbig for (int ich = 0; ich < nchannels; ich++) {
260*6897da5cSDirk Helbig int frame_bytes = block_bytes / nchannels
261*6897da5cSDirk Helbig + (ich < block_bytes % nchannels);
2624930cef6SMatthias Ringwald
263*6897da5cSDirk Helbig lc3_encode(enc[ich],
264*6897da5cSDirk Helbig pcm_fmt, pcm + ich * pcm_sbytes, nchannels,
265*6897da5cSDirk Helbig frame_bytes, out_ptr);
266*6897da5cSDirk Helbig
267*6897da5cSDirk Helbig out_ptr += frame_bytes;
268*6897da5cSDirk Helbig }
269*6897da5cSDirk Helbig
270*6897da5cSDirk Helbig lc3bin_write_data(fp_out, out, block_bytes);
2714930cef6SMatthias Ringwald }
2724930cef6SMatthias Ringwald
2734930cef6SMatthias Ringwald unsigned t = (clock_us() - t0) / 1000;
2744930cef6SMatthias Ringwald nsec = encode_samples / srate_hz;
2754930cef6SMatthias Ringwald
2764930cef6SMatthias Ringwald fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n",
2774930cef6SMatthias Ringwald nsec / 60, nsec % 60, t / 1000, t % 1000, "");
2784930cef6SMatthias Ringwald
2794930cef6SMatthias Ringwald /* --- Cleanup --- */
2804930cef6SMatthias Ringwald
281*6897da5cSDirk Helbig for (int ich = 0; ich < nchannels; ich++)
2824930cef6SMatthias Ringwald free(enc[ich]);
2834930cef6SMatthias Ringwald
2844930cef6SMatthias Ringwald if (fp_in != stdin)
2854930cef6SMatthias Ringwald fclose(fp_in);
2864930cef6SMatthias Ringwald
2874930cef6SMatthias Ringwald if (fp_out != stdout)
2884930cef6SMatthias Ringwald fclose(fp_out);
2894930cef6SMatthias Ringwald }
290