63 return std::unexpected(
ready.error());
73 captureEncoder_ = std::make_unique<gsm610::Encoder>();
74 if (!captureEncoder_->available())
76 captureEncoder_.reset();
77 return std::unexpected(
"libgsm WAV49 encoder is unavailable");
81 captureEncoder_.reset();
91 if (captureStream_ ==
nullptr)
93 captureEncoder_.reset();
94 return std::unexpected(
95 sdlFailure(
"SDL_OpenAudioDeviceStream(recording)"));
101 sdlFailure(
"SDL_ResumeAudioStreamDevice(recording)");
103 captureStream_ =
nullptr;
104 captureEncoder_.reset();
105 return std::unexpected(
error);
108 postSilenceState_ = PostSilenceState::Silence;
111 postSilenceStorage_.clear();
112 const auto started = sendStartPacket();
116 captureStream_ =
nullptr;
117 captureEncoder_.reset();
125 if (captureStream_ !=
nullptr)
131 captureStream_ =
nullptr;
133 captureEncoder_.reset();
134 postSilenceStorage_.clear();
135 postSilenceState_ = PostSilenceState::Silence;
142 return captureStream_ !=
nullptr;
145 std::expected<void, std::string> AudioRuntime::sendStartPacket()
148 std::span<const std::uint8_t> extra{};
149 std::array<std::uint8_t, 2>
gsmExtra{};
171 return std::unexpected(
172 "voice CHAT packet exceeds the retail 64K limit");
174 return std::unexpected(
175 "unable to queue the initial voice CHAT packet");
179 std::expected<void, std::string> AudioRuntime::sendData(
186 return std::unexpected(
187 "voice data packet exceeds the retail 64K limit");
195 bool AudioRuntime::containsNoise(
196 std::span<const std::uint8_t>
bytes)
const noexcept
200 std::uint64_t
total{};
205 const int sample =
static_cast<int>(
bytes[
offset]) - 128;
206 total +=
static_cast<std::uint64_t
>(std::abs(sample));
212 static_cast<float>(
count) / 128.0F;
213 return average > settings_.silenceThreshold;
216 void AudioRuntime::appendHerald(std::vector<std::uint8_t>&
bytes)
const
218 const std::size_t
samples =
static_cast<std::size_t
>(
229 const std::size_t local =
243 const auto phase =
static_cast<std::uint8_t
>(
254 if (captureStream_ ==
nullptr)
259 return std::unexpected(
260 sdlFailure(
"SDL_GetAudioStreamAvailable(recording)"));
262 if (available < minimum)
267 std::vector<std::uint8_t>
samples(
272 return std::unexpected(
273 sdlFailure(
"SDL_GetAudioStreamData(recording)"));
280 lastNoiseTick_ = tick;
283 postSilenceState_ = PostSilenceState::Silence;
284 postSilenceStorage_.clear();
288 if (postSilenceState_ == PostSilenceState::Silence)
292 postSilenceState_ = PostSilenceState::Collecting;
293 firstNoiseTick_ = lastNoiseTick_;
300 if (!captureEncoder_)
301 return std::unexpected(
"GSM 6.10 capture encoder was lost");
304 return std::unexpected(
encoded.error());
313 if (postSilenceState_ == PostSilenceState::Collecting)
318 std::min(postSilenceStorage_.size(),
323 postSilenceStorage_.insert(postSilenceStorage_.end(),
328 if (!postSilenceStorage_.empty())
330 const auto first = sendData(postSilenceStorage_,
true);
332 postSilenceStorage_.clear();
333 const auto current = sendData(
wireData,
false);
334 if (!current)
return current;
341 postSilenceState_ = PostSilenceState::Streaming;
348 std::expected<void, std::string> AudioRuntime::openReceiver(
353 return std::unexpected(
354 "unsupported GSM 6.10 WAVEFORMATEX parameters");
356 return std::unexpected(
"unsupported voice-chat wave format");
363 return std::unexpected(
"invalid PCM voice-chat format");
367 return std::unexpected(
ready.error());
374 spec.freq =
static_cast<int>(
event.format.samplesPerSecond);
379 spec.channels =
static_cast<int>(
event.format.channels);
380 spec.freq =
static_cast<int>(
event.format.samplesPerSecond);
385 if (stream ==
nullptr)
386 return std::unexpected(
387 sdlFailure(
"SDL_OpenAudioDeviceStream(voice playback)"));
389 auto receiver = std::make_unique<Receiver>();
391 receiver->encodedFormat =
event.format;
393 event.format.averageBytesPerSecond / 4u;
396 receiver->maxFeedingSize = std::max<std::uint32_t>(
400 receiver->gsmDecoder = std::make_unique<gsm610::Decoder>();
401 if (!
receiver->gsmDecoder->available())
402 return std::unexpected(
403 "libgsm WAV49 decoder is unavailable");
406 return std::unexpected(
407 sdlFailure(
"SDL_ResumeAudioStreamDevice(voice playback)"));
408 receivers_[sourceId] = std::move(
receiver);
412 bool AudioRuntime::feedReceiver(
416 const auto align = std::max<std::uint16_t>(
417 receiver.encodedFormat.blockAlign, 1u);
430 const std::size_t amount = std::min<std::size_t>(
444 input.data(),
static_cast<int>(
input.size())))
457 receivers_.erase(sourceId);
458 return static_cast<bool>(openReceiver(event, sourceId));
461 const auto found = receivers_.find(sourceId);
464 if (
found != receivers_.end())
465 receivers_.erase(
found);
468 if (
found == receivers_.end())
476 static_cast<float>(std::min(event.
value, 100u)) / 100.0F);
std::vector< std::byte > bytes
SDL_GPUShaderFormat format
std::expected< void, std::string > ensureReady()
void stopCapture() noexcept
AudioRuntime(audio::Runtime &audio) noexcept
bool captureActive() const noexcept
std::expected< void, std::string > pumpCapture(std::uint64_t tick)
std::expected< void, std::string > startCapture(Settings settings={})
bool handleEvent(const packet::Event &event, std::uint32_t sourceId)
void closeAllReceivers() noexcept
constexpr std::uint16_t FormatTag
bool matchesRetailWireFormat(const packet::Event &event) noexcept
constexpr WireFormat retailWireFormat() noexcept
bool makeDataPacket(std::span< const std::uint8_t > data, bool afterSilence, std::vector< std::uint8_t > &packet)
bool makeStartPacket(WaveFormat format, std::span< const std::uint8_t > extraFormatBytes, std::uint32_t dimensions, std::uint32_t volume, std::vector< std::uint8_t > &packet)
bool makeStopPacket(std::vector< std::uint8_t > &packet)
bool sendPacket(std::span< const std::uint8_t > payload, bool importantDataDoNotDiscard)
std::unique_ptr< gsm610::Decoder > gsmDecoder
std::uint32_t maxFeedingSize
packet::WaveFormat encodedFormat
std::uint32_t heraldDelayTicks
std::uint32_t postSilenceDelayTicks
std::uint32_t maxSilentTicks
std::span< const std::uint8_t > payload