Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
Gsm610Codec.cpp
Go to the documentation of this file.
1#include "Gsm610Codec.hpp"
2
3extern "C"
4{
5#include <gsm.h>
6}
7
8#include <algorithm>
9#include <array>
10#include <utility>
11
13{
14 namespace
15 {
16 [[nodiscard]] gsm createWav49State() noexcept
17 {
19 if (state == nullptr)
20 return nullptr;
21 int enabled = 1;
23 {
25 return nullptr;
26 }
27 return state;
28 }
29
30 [[nodiscard]] gsm_signal toSignal(std::uint8_t sample) noexcept
31 {
32 return static_cast<gsm_signal>(
33 (static_cast<int>(sample) - 128) * 256);
34 }
35
36 [[nodiscard]] std::uint8_t toU8(gsm_signal sample) noexcept
37 {
38 const int value = static_cast<int>(sample) / 256 + 128;
39 return static_cast<std::uint8_t>(std::clamp(value, 0, 255));
40 }
41 }
42
43 bool matchesRetailWireFormat(const packet::Event& event) noexcept
44 {
45 if (event.format.formatTag != FormatTag ||
46 event.format.channels != 1u ||
47 event.format.samplesPerSecond != SampleRate ||
48 event.format.averageBytesPerSecond != AverageBytesPerSecond ||
49 event.format.blockAlign != BlockAlign ||
50 event.format.bitsPerSample != 0u ||
51 event.format.extraSize < 2u ||
52 event.payload.size() < 20u)
53 return false;
54
55 return packet::readLe16(event.payload, 18u) == SamplesPerBlock;
56 }
57
59 {
61
63 {
64 if (state != nullptr)
66 }
67 };
68
70 : impl_(std::make_unique<Impl>())
71 {
72 }
73
74 Encoder::~Encoder() = default;
77
78 bool Encoder::available() const noexcept
79 {
80 return impl_ && impl_->state != nullptr;
81 }
82
84 {
85 pending_.clear();
86 impl_ = std::make_unique<Impl>();
87 }
88
89 std::expected<std::vector<std::uint8_t>, std::string>
90 Encoder::encodeU8(std::span<const std::uint8_t> pcm)
91 {
92 if (!impl_ || impl_->state == nullptr)
93 return std::unexpected("libgsm WAV49 encoder is unavailable");
94
95 pending_.insert(pending_.end(), pcm.begin(), pcm.end());
96 const std::size_t completeBlocks = pending_.size() / SamplesPerBlock;
97 std::vector<std::uint8_t> encoded;
99
100 std::array<gsm_signal, SamplesPerFrame> samples{};
101 std::array<gsm_byte, 33> frame{};
102 std::size_t offset{};
103 for (std::size_t block = 0; block < completeBlocks; ++block)
104 {
105 for (std::size_t index = 0; index < SamplesPerFrame; ++index)
106 samples[index] = toSignal(pending_[offset + index]);
107 gsm_encode(impl_->state, samples.data(), frame.data());
108 encoded.insert(encoded.end(), frame.begin(), frame.begin() + 32);
110
111 for (std::size_t index = 0; index < SamplesPerFrame; ++index)
112 samples[index] = toSignal(pending_[offset + index]);
113 gsm_encode(impl_->state, samples.data(), frame.data());
114 encoded.insert(encoded.end(), frame.begin(), frame.end());
116 }
117
118 if (offset != 0u)
119 pending_.erase(pending_.begin(), pending_.begin() +
120 static_cast<std::ptrdiff_t>(offset));
121 return encoded;
122 }
123
125 {
127
129 {
130 if (state != nullptr)
132 }
133 };
134
136 : impl_(std::make_unique<Impl>())
137 {
138 }
139
140 Decoder::~Decoder() = default;
143
144 bool Decoder::available() const noexcept
145 {
146 return impl_ && impl_->state != nullptr;
147 }
148
150 {
151 impl_ = std::make_unique<Impl>();
152 }
153
154 std::expected<std::vector<std::uint8_t>, std::string>
155 Decoder::decodeToU8(std::span<const std::uint8_t> encoded)
156 {
157 if (!impl_ || impl_->state == nullptr)
158 return std::unexpected("libgsm WAV49 decoder is unavailable");
159 if (encoded.size() % BlockAlign != 0u)
160 return std::unexpected("GSM 6.10 payload is not block aligned");
161
162 std::vector<std::uint8_t> pcm;
163 pcm.reserve(encoded.size() / BlockAlign * SamplesPerBlock);
164 std::array<gsm_signal, SamplesPerFrame> samples{};
165
166 for (std::size_t offset = 0; offset < encoded.size(); offset += BlockAlign)
167 {
168 auto* first = const_cast<gsm_byte*>(
169 reinterpret_cast<const gsm_byte*>(encoded.data() + offset));
170 if (gsm_decode(impl_->state, first, samples.data()) < 0)
171 return std::unexpected("invalid first GSM 6.10 WAV49 frame");
172 for (const auto sample : samples)
173 pcm.push_back(toU8(sample));
174
175 auto* second = const_cast<gsm_byte*>(
176 reinterpret_cast<const gsm_byte*>(encoded.data() + offset + 33u));
177 if (gsm_decode(impl_->state, second, samples.data()) < 0)
178 return std::unexpected("invalid second GSM 6.10 WAV49 frame");
179 for (const auto sample : samples)
180 pcm.push_back(toU8(sample));
181 }
182 return pcm;
183 }
184}
std::size_t offset
bool enabled
Definition Engine.cpp:170
unsigned state
unsigned index
data::DataTag first
std::size_t frame
std::uint16_t value
Definition Timers.cpp:16
std::expected< std::vector< std::uint8_t >, std::string > decodeToU8(std::span< const std::uint8_t > encoded)
std::expected< std::vector< std::uint8_t >, std::string > encodeU8(std::span< const std::uint8_t > pcm)
constexpr std::uint16_t SamplesPerBlock
constexpr std::uint32_t SampleRate
constexpr std::uint16_t FormatTag
constexpr std::uint16_t BlockAlign
constexpr std::uint32_t AverageBytesPerSecond
constexpr std::uint16_t SamplesPerFrame
bool matchesRetailWireFormat(const packet::Event &event) noexcept
std::uint16_t readLe16(std::span< const std::uint8_t > bytes, std::size_t offset) noexcept