codec-amr
AMRNB.h
1 #pragma once
2 
3 #include <string.h>
4 
5 #include "AMRCodec.h"
6 extern "C" {
7 #include "nb/interf_dec.h"
8 #include "nb/interf_enc.h"
9 }
10 
14 class AMRNB : public AMRCodec {
15  public:
19  enum class Mode {
20  NB_475 = 0, // 4.75 kbit/s
21  NB_515, // 5.15 kbit/s
22  NB_59, // 5.9 kbit/s
23  NB_67, // 6.7 kbit/s
24  NB_74, // 7.4 kbit/s
25  NB_795, // 7.95 kbit/s
26  NB_102, // 10.2 kbit/s
27  NB_122 // 12.2 kbit/s
28  };
29 
34  inline AMRNB(bool enableDTX = false) { dtx_enabled = enableDTX; }
35 
39  inline ~AMRNB() override {
40  if (encoderState) {
41  Encoder_Interface_exit(encoderState);
42  encoderState = nullptr;
43  }
44 
45  if (decoderState) {
46  Decoder_Interface_exit(decoderState);
47  decoderState = nullptr;
48  }
49  }
50 
55  inline void setMode(Mode mode) { currentMode = mode; }
56 
61  inline Mode getMode() const { return currentMode; }
62 
71  inline int encode(const int16_t* pcmSamples, size_t sampleCount,
72  uint8_t* amrData, size_t amrBufferSize) override {
73  // Initialize the encoder
74  if (encoderState == nullptr)
75  encoderState = Encoder_Interface_init(dtx_enabled ? 1 : 0);
76 
77  // Check if we can continue
78  if (encoderState == nullptr || !amrData || amrBufferSize == 0) {
79  return 0;
80  }
81 
82  // AMR-NB works with 160 samples per frame (20ms at 8kHz)
83  const size_t samplesPerFrame = 160;
84  size_t bytesWritten = 0;
85 
86  for (size_t i = 0; i < sampleCount && bytesWritten < amrBufferSize;
87  i += samplesPerFrame) {
88  // Make sure we have a full frame
89  if (i + samplesPerFrame > sampleCount) {
90  break;
91  }
92 
93  // Check if we have enough space for worst-case frame size (32 bytes)
94  if (bytesWritten + 32 > amrBufferSize) {
95  break;
96  }
97 
98  // Encode the frame
99  int frameBytes = Encoder_Interface_Encode(
100  encoderState, mapMode(currentMode),
101  const_cast<short*>(pcmSamples + i), amrData + bytesWritten,
102  0 // forceSpeech
103  );
104 
105  bytesWritten += frameBytes;
106  }
107 
108  return bytesWritten;
109  }
110 
119  inline int decode(const uint8_t* amrData, size_t amrSize, int16_t* pcmSamples,
120  size_t maxSampleCount) override {
121  // Initialize the encoder and decoder
122  if (decoderState == nullptr) decoderState = Decoder_Interface_init();
123  // Check if we can continue
124  if (decoderState == nullptr || !amrData || amrSize == 0 || !pcmSamples ||
125  maxSampleCount == 0) {
126  return 0;
127  }
128 
129  // AMR-NB produces 160 samples per frame (20ms at 8kHz)
130  const size_t samplesPerFrame = 160;
131  size_t totalSamplesDecoded = 0;
132  size_t offset = 0;
133 
134  // Process the input buffer frame by frame
135  while (offset < amrSize &&
136  totalSamplesDecoded + samplesPerFrame <= maxSampleCount) {
137  // Extract frame type from the frame header
138  uint8_t frameType = (amrData[offset] >> 3) & 0x0F;
139 
140  // Find frame size - this is just an approximation
141  size_t frameSize = 1;
142  if (frameType <= 7) {
143  frameSize = getEncodedFrameSizeBytes(frameType);
144 
145  }
146 
147  if (offset + frameSize > amrSize) {
148  break; // Not enough data for another frame
149  }
150 
151  // Decode this frame
152  Decoder_Interface_Decode(decoderState,
153  const_cast<unsigned char*>(amrData + offset),
154  pcmSamples + totalSamplesDecoded,
155  0 // BFI (bad frame indicator)
156  );
157 
158  offset += frameSize;
159  totalSamplesDecoded += samplesPerFrame;
160  }
161 
162  return totalSamplesDecoded;
163  }
164 
169  inline int getSampleRate() const override { return 8000; }
170 
175  int getFrameSizeSamples() override { return 160; }
181  int getEncodedFrameSizeBytes() override {
182  return getEncodedFrameSizeBytes(static_cast<int>(currentMode));
183  }
184 
185  private:
186  void* encoderState = nullptr;
187  void* decoderState = nullptr;
188  Mode currentMode = Mode::NB_122;
189  bool dtx_enabled = false;
190 
191  // Map C++ enum to C API enum
192  inline enum ModeNB mapMode(AMRNB::Mode mode) {
193  return static_cast<ModeNB>(mode);
194  }
195 
196  inline int getEncodedFrameSizeBytes(int mode) {
197  // Bytes per encoded frame for each mode
198  const uint8_t frameSizes[] = {
199  13, // MR475 (4.75 kbps)
200  14, // MR515 (5.15 kbps)
201  16, // MR59 (5.9 kbps)
202  18, // MR67 (6.7 kbps)
203  20, // MR74 (7.4 kbps)
204  21, // MR795 (7.95 kbps)
205  27, // MR102 (10.2 kbps)
206  32 // MR122 (12.2 kbps)
207  };
208  return frameSizes[static_cast<int>(mode)];
209  }
210 };
Base class for AMR codec implementations.
Definition: AMRCodec.h:9
AMR Narrowband codec implementation.
Definition: AMRNB.h:14
~AMRNB() override
Destructor.
Definition: AMRNB.h:39
int decode(const uint8_t *amrData, size_t amrSize, int16_t *pcmSamples, size_t maxSampleCount) override
Decode AMR-NB data to PCM samples.
Definition: AMRNB.h:119
int getEncodedFrameSizeBytes() override
Get the size in bytes for one encoded frame in current mode.
Definition: AMRNB.h:181
void setMode(Mode mode)
Set encoding mode.
Definition: AMRNB.h:55
int getFrameSizeSamples() override
Get frame size in samples (160 for AMR-NB)
Definition: AMRNB.h:175
Mode
Available encoding modes for AMR-NB.
Definition: AMRNB.h:19
AMRNB(bool enableDTX=false)
Construct a new AMRNB codec.
Definition: AMRNB.h:34
Mode getMode() const
Get current encoding mode.
Definition: AMRNB.h:61
int encode(const int16_t *pcmSamples, size_t sampleCount, uint8_t *amrData, size_t amrBufferSize) override
Encode PCM samples to AMR-NB format.
Definition: AMRNB.h:71
int getSampleRate() const override
Get sample rate (8000 Hz for AMR-NB)
Definition: AMRNB.h:169