#ifndef __LC3_CPP_H
#define __LC3_CPP_H
#include <cassert>
#include <memory>
#include <vector>
#include <stdlib.h>
#include "lc3.h"
namespace lc3 {
enum class PcmFormat {
kS16 = LC3_PCM_FORMAT_S16,
kS24 = LC3_PCM_FORMAT_S24,
kS24In3Le = LC3_PCM_FORMAT_S24_3LE,
kF32 = LC3_PCM_FORMAT_FLOAT
};
template <typename T>
class Base {
protected:
Base(int dt_us, int sr_hz, int sr_pcm_hz, size_t nchannels, bool hrmode)
: dt_us_(dt_us),
sr_hz_(sr_hz),
sr_pcm_hz_(sr_pcm_hz == 0 ? sr_hz : sr_pcm_hz),
nchannels_(nchannels),
hrmode_(hrmode) {
states.reserve(nchannels_);
}
virtual ~Base() = default;
int dt_us_, sr_hz_;
int sr_pcm_hz_;
size_t nchannels_;
bool hrmode_;
using state_ptr = std::unique_ptr<T, decltype(&free)>;
std::vector<state_ptr> states;
public:
int GetFrameSamples() {
return lc3_hr_frame_samples(hrmode_, dt_us_, sr_pcm_hz_); }
int GetFrameBytes(int bitrate) {
return lc3_hr_frame_bytes(hrmode_, dt_us_, sr_hz_, bitrate); }
int ResolveBitrate(int nbytes) {
return lc3_hr_resolve_bitrate(hrmode_, dt_us_, sr_hz_, nbytes); }
int GetDelaySamples() {
return lc3_hr_delay_samples(hrmode_, dt_us_, sr_pcm_hz_); }
};
class Encoder : public Base<struct lc3_encoder> {
template <typename T>
int EncodeImpl(PcmFormat fmt, const T *pcm, int frame_size, uint8_t *out) {
if (states.size() != nchannels_) return -1;
enum lc3_pcm_format cfmt = static_cast<lc3_pcm_format>(fmt);
int ret = 0;
for (size_t ich = 0; ich < nchannels_; ich++)
ret |= lc3_encode(states[ich].get(), cfmt, pcm + ich, nchannels_,
frame_size, out + ich * frame_size);
return ret;
}
public:
Encoder(int dt_us, int sr_hz, int sr_pcm_hz = 0,
size_t nchannels = 1, bool hrmode = false)
: Base(dt_us, sr_hz, sr_pcm_hz, nchannels, hrmode) {
for (size_t ich = 0; ich < nchannels_; ich++) {
auto s = state_ptr((lc3_encoder_t)
malloc(lc3_hr_encoder_size(hrmode_, dt_us_, sr_pcm_hz_)), free);
if (lc3_hr_setup_encoder(hrmode_, dt_us_, sr_hz_, sr_pcm_hz_, s.get()))
states.push_back(std::move(s));
}
}
~Encoder() override = default;
void Reset() {
for (auto &s : states)
lc3_hr_setup_encoder(hrmode_, dt_us_, sr_hz_, sr_pcm_hz_, s.get());
}
int Encode(const int16_t *pcm, int frame_size, uint8_t *out) {
return EncodeImpl(PcmFormat::kS16, pcm, frame_size, out);
}
int Encode(const int32_t *pcm, int frame_size, uint8_t *out) {
return EncodeImpl(PcmFormat::kS24, pcm, frame_size, out);
}
int Encode(const float *pcm, int frame_size, uint8_t *out) {
return EncodeImpl(PcmFormat::kF32, pcm, frame_size, out);
}
int Encode(PcmFormat fmt, const void *pcm, int frame_size, uint8_t *out) {
uintptr_t pcm_ptr = reinterpret_cast<uintptr_t>(pcm);
switch (fmt) {
case PcmFormat::kS16:
assert(pcm_ptr % alignof(int16_t) == 0);
return EncodeImpl(fmt, reinterpret_cast<const int16_t *>(pcm),
frame_size, out);
case PcmFormat::kS24:
assert(pcm_ptr % alignof(int32_t) == 0);
return EncodeImpl(fmt, reinterpret_cast<const int32_t *>(pcm),
frame_size, out);
case PcmFormat::kS24In3Le:
return EncodeImpl(fmt, reinterpret_cast<const int8_t(*)[3]>(pcm),
frame_size, out);
case PcmFormat::kF32:
assert(pcm_ptr % alignof(float) == 0);
return EncodeImpl(fmt, reinterpret_cast<const float *>(pcm), frame_size,
out);
}
return -1;
}
};
class Decoder : public Base<struct lc3_decoder> {
template <typename T>
int DecodeImpl(const uint8_t *in, int frame_size, PcmFormat fmt, T *pcm) {
if (states.size() != nchannels_) return -1;
enum lc3_pcm_format cfmt = static_cast<enum lc3_pcm_format>(fmt);
int ret = 0;
for (size_t ich = 0; ich < nchannels_; ich++)
ret |= lc3_decode(states[ich].get(), in + ich * frame_size, frame_size,
cfmt, pcm + ich, nchannels_);
return ret;
}
public:
Decoder(int dt_us, int sr_hz, int sr_pcm_hz = 0,
size_t nchannels = 1, bool hrmode = false)
: Base(dt_us, sr_hz, sr_pcm_hz, nchannels, hrmode) {
for (size_t i = 0; i < nchannels_; i++) {
auto s = state_ptr((lc3_decoder_t)
malloc(lc3_hr_decoder_size(hrmode_, dt_us_, sr_pcm_hz_)), free);
if (lc3_hr_setup_decoder(hrmode_, dt_us_, sr_hz_, sr_pcm_hz_, s.get()))
states.push_back(std::move(s));
}
}
~Decoder() override = default;
void Reset() {
for (auto &s : states)
lc3_hr_setup_decoder(hrmode_, dt_us_, sr_hz_, sr_pcm_hz_, s.get());
}
int Decode(const uint8_t *in, int frame_size, int16_t *pcm) {
return DecodeImpl(in, frame_size, PcmFormat::kS16, pcm);
}
int Decode(const uint8_t *in, int frame_size, int32_t *pcm) {
return DecodeImpl(in, frame_size, PcmFormat::kS24In3Le, pcm);
}
int Decode(const uint8_t *in, int frame_size, float *pcm) {
return DecodeImpl(in, frame_size, PcmFormat::kF32, pcm);
}
int Decode(const uint8_t *in, int frame_size, PcmFormat fmt, void *pcm) {
uintptr_t pcm_ptr = reinterpret_cast<uintptr_t>(pcm);
switch (fmt) {
case PcmFormat::kS16:
assert(pcm_ptr % alignof(int16_t) == 0);
return DecodeImpl(in, frame_size, fmt,
reinterpret_cast<int16_t *>(pcm));
case PcmFormat::kS24:
assert(pcm_ptr % alignof(int32_t) == 0);
return DecodeImpl(in, frame_size, fmt,
reinterpret_cast<int32_t *>(pcm));
case PcmFormat::kS24In3Le:
return DecodeImpl(in, frame_size, fmt,
reinterpret_cast<int8_t(*)[3]>(pcm));
case PcmFormat::kF32:
assert(pcm_ptr % alignof(float) == 0);
return DecodeImpl(in, frame_size, fmt, reinterpret_cast<float *>(pcm));
}
return -1;
}
};
}
#endif