Files
liblc3/tools/elc3.c
Taruntej Kanakamalla 22626a595b tools/elc3: Use correct offset for pcm buffer padding
The address offset of the pcm buffer computed is in samples
instead of bytes. This causes the last few bytes of the original pcm
content to get corrupted with the padding bytes.
Pass the number of bytes read instead of samples
as the address offset.
2022-11-10 11:03:01 +05:30

261 lines
7.1 KiB
C

/******************************************************************************
*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#define _POSIX_C_SOURCE 199309L
#include <stdalign.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <lc3.h>
#include "lc3bin.h"
#include "wave.h"
/**
* Error handling
*/
static void error(int status, const char *format, ...)
{
va_list args;
fflush(stdout);
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
exit(status);
}
/**
* Parameters
*/
struct parameters {
const char *fname_in;
const char *fname_out;
float frame_ms;
int srate_hz;
int bitrate;
};
static struct parameters parse_args(int argc, char *argv[])
{
static const char *usage =
"Usage: %s [options] [wav_file] [out_file]\n"
"\n"
"wav_file\t" "Input wave file, stdin if omitted\n"
"out_file\t" "Output bitstream file, stdout if omitted\n"
"\n"
"Options:\n"
"\t-h\t" "Display help\n"
"\t-b\t" "Bitrate in bps (mandatory)\n"
"\t-m\t" "Frame duration in ms (default 10)\n"
"\t-r\t" "Encoder samplerate (default is input samplerate)\n"
"\n";
struct parameters p = { .frame_ms = 10 };
for (int iarg = 1; iarg < argc; ) {
const char *arg = argv[iarg++];
if (arg[0] == '-') {
if (arg[2] != '\0')
error(EINVAL, "Option %s", arg);
char opt = arg[1];
const char *optarg;
switch (opt) {
case 'b': case 'm': case 'r':
if (iarg >= argc)
error(EINVAL, "Argument %s", arg);
optarg = argv[iarg++];
}
switch (opt) {
case 'h': fprintf(stderr, usage, argv[0]); exit(0);
case 'b': p.bitrate = atoi(optarg); break;
case 'm': p.frame_ms = atof(optarg); break;
case 'r': p.srate_hz = atoi(optarg); break;
default:
error(EINVAL, "Option %s", arg);
}
} else {
if (!p.fname_in)
p.fname_in = arg;
else if (!p.fname_out)
p.fname_out = arg;
else
error(EINVAL, "Argument %s", arg);
}
}
return p;
}
/**
* Return time in (us) from unspecified point in the past
*/
static unsigned clock_us(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
}
/**
* Entry point
*/
int main(int argc, char *argv[])
{
/* --- Read parameters --- */
struct parameters p = parse_args(argc, argv);
FILE *fp_in = stdin, *fp_out = stdout;
if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
error(errno, "%s", p.fname_in);
if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
error(errno, "%s", p.fname_out);
if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz))
error(EINVAL, "Samplerate %d Hz", p.srate_hz);
/* --- Check parameters --- */
int frame_us = p.frame_ms * 1000;
int srate_hz, nch, nsamples;
int pcm_sbits, pcm_sbytes;
if (wave_read_header(fp_in,
&pcm_sbits, &pcm_sbytes, &srate_hz, &nch, &nsamples) < 0)
error(EINVAL, "Bad or unsupported WAVE input file");
if (p.bitrate <= 0)
error(EINVAL, "Bitrate");
if (!LC3_CHECK_DT_US(frame_us))
error(EINVAL, "Frame duration");
if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz > srate_hz))
error(EINVAL, "Samplerate %d Hz", srate_hz);
if (pcm_sbits != 16 && pcm_sbits != 24)
error(EINVAL, "Bitdepth %d", pcm_sbits);
if ((pcm_sbits == 16 && pcm_sbytes != 16/8) ||
(pcm_sbits == 24 && pcm_sbytes != 24/8 && pcm_sbytes != 32/8))
error(EINVAL, "Sample storage on %d bytes", pcm_sbytes);
if (nch < 1 || nch > 2)
error(EINVAL, "Number of channels %d", nch);
int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
int enc_samples = !p.srate_hz ? nsamples :
((int64_t)nsamples * enc_srate_hz) / srate_hz;
lc3bin_write_header(fp_out,
frame_us, enc_srate_hz, p.bitrate, nch, enc_samples);
/* --- Setup encoding --- */
int frame_bytes = lc3_frame_bytes(frame_us, p.bitrate / nch);
int frame_samples = lc3_frame_samples(frame_us, srate_hz);
int encode_samples = nsamples + lc3_delay_samples(frame_us, srate_hz);
lc3_encoder_t enc[nch];
int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes];
enum lc3_pcm_format pcm_fmt =
pcm_sbytes == 32/8 ? LC3_PCM_FORMAT_S24 :
pcm_sbytes == 24/8 ? LC3_PCM_FORMAT_S24_3LE : LC3_PCM_FORMAT_S16;
uint8_t out[nch][frame_bytes];
for (int ich = 0; ich < nch; ich++)
enc[ich] = lc3_setup_encoder(frame_us, enc_srate_hz, srate_hz,
malloc(lc3_encoder_size(frame_us, srate_hz)));
/* --- Encoding loop --- */
static const char *dash_line = "========================================";
int nsec = 0;
unsigned t0 = clock_us();
for (int i = 0; i * frame_samples < encode_samples; i++) {
int nread = wave_read_pcm(fp_in, pcm_sbytes, nch, frame_samples, pcm);
memset(pcm + nread * nch * pcm_sbytes, 0,
nch * (frame_samples - nread) * pcm_sbytes);
if (floorf(i * frame_us * 1e-6) > nsec) {
float progress = fminf(
(float)i * frame_samples / encode_samples, 1);
fprintf(stderr, "%02d:%02d [%-40s]\r",
nsec / 60, nsec % 60,
dash_line + (int)floorf((1 - progress) * 40));
nsec = (int)(i * frame_us * 1e-6);
}
for (int ich = 0; ich < nch; ich++)
lc3_encode(enc[ich],
pcm_fmt, pcm + ich * pcm_sbytes, nch,
frame_bytes, out[ich]);
lc3bin_write_data(fp_out, out, nch, frame_bytes);
}
unsigned t = (clock_us() - t0) / 1000;
nsec = encode_samples / srate_hz;
fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n",
nsec / 60, nsec % 60, t / 1000, t % 1000, "");
/* --- Cleanup --- */
for (int ich = 0; ich < nch; ich++)
free(enc[ich]);
if (fp_in != stdin)
fclose(fp_in);
if (fp_out != stdout)
fclose(fp_out);
}