Fixes for 1.4.6

This commit is contained in:
WolverinDEV 2020-05-01 20:03:55 +02:00
parent b92f584247
commit d60eecd9a9
15 changed files with 242 additions and 42 deletions

2
github

@ -1 +1 @@
Subproject commit cca898581e4b4138a2b4f99429d0bbd70a344bc4 Subproject commit 30322f64162fd4805bc01542cc5cefbb9fe842a3

View File

@ -119,7 +119,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
support_latency_settings() { return true; }, support_latency_settings() { return true; },
reset_latency_settings: function() { reset_latency_settings: function() {
const stream = this.get_stream(); const stream = this.get_stream();
stream.set_buffer_latency(0.02); stream.set_buffer_latency(0.040);
stream.set_buffer_max_latency(0.2); stream.set_buffer_max_latency(0.2);
return this.latency_settings(); return this.latency_settings();
}, },

View File

@ -13,6 +13,56 @@ using namespace tc::audio;
void AudioOutputSource::clear() { void AudioOutputSource::clear() {
this->buffer.clear(); this->buffer.clear();
this->buffering = true; this->buffering = true;
this->fade_in_start = this->buffer.write_ptr();
}
void AudioOutputSource::do_fade_out(size_t pop_count) {
if(this->will_buffer_in != -1) return;
_test_for_fade:
const auto samples_left = this->current_latency();
if(samples_left < this->fadeout_sample_length + pop_count) {
if(auto fn = this->on_underflow; fn && fn(0))
goto _test_for_fade;
auto wptr = (float*) this->buffer.write_ptr();
auto total_samples = std::min(samples_left, this->fadeout_sample_length);
if(total_samples == 0) return; //TODO Test against min_buffered_samples
for(size_t index{0}; index < total_samples; index++) {
const auto offset = (float) ((float) index / (float) total_samples);
const auto volume = log10f(1 - offset) / -2.71828182845904f;
for(int channel{0}; channel < this->channel_count; channel++)
*wptr-- *= volume;
}
log_trace(category::audio, tr("Will buffer due to fade out ({} | {})"), total_samples, *(float*) this->buffer.write_ptr());
this->will_buffer_in = total_samples;
}
}
void AudioOutputSource::do_fade_in() {
if(!this->fade_in_start)
return;
const auto samples_available = this->current_latency();
auto wptr = (float*) this->fade_in_start;
auto total_samples = std::min(samples_available, this->fadeout_sample_length);
if(total_samples == 0) {
log_trace(category::audio, tr("Ignoring fade in 0: {} {}"), samples_available, this->fadeout_sample_length);
return;
}
for(size_t index{0}; index < total_samples; index++) {
const auto offset = (float) ((float) index / (float) total_samples);
const auto volume = log10f(1 - offset) / -2.71828182845904f;
for(int channel{0}; channel < this->channel_count; channel++)
*wptr++ *= volume;
}
log_trace(category::audio, tr("Fade in to new buffer ({})"), total_samples);
this->fade_in_start = nullptr;
} }
ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) { ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
@ -21,12 +71,27 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
load_buffer: load_buffer:
auto available_bytes = this->buffer.fill_count(); auto available_bytes = this->buffer.fill_count();
if(available_bytes < sizeof(float) * this->channel_count) return written; if(available_bytes < sizeof(float) * this->channel_count) return written;
auto available_samples = available_bytes / sizeof(float) / this->channel_count; auto available_samples = available_bytes / sizeof(float) / this->channel_count;
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering);
if(this->buffering && available_samples < this->min_buffered_samples) return -2;
this->buffering = false; if(this->buffering && available_samples < this->min_buffered_samples) return -2;
this->do_fade_in();
this->do_fade_out(samples); /* will also call for underflow */
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {} Required: {}, left: {}, will buffer in {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering, samples, (int) available_samples - samples, this->will_buffer_in);
if(this->will_buffer_in > 0) {
if(samples > this->will_buffer_in) {
samples = this->will_buffer_in;
this->buffering = true;
this->fade_in_start = this->buffer.calculate_advanced_write_ptr(samples * sizeof(float) * this->channel_count);
this->will_buffer_in = -1;
log_trace(category::audio, tr("Start buffering due to fade out. Fade in ptr {}"), (void*) this->fade_in_start);
} else {
this->will_buffer_in -= samples;
}
} else {
this->buffering = false;
}
if(available_samples >= samples - written) { if(available_samples >= samples - written) {
const auto byte_length = (samples - written) * sizeof(float) * this->channel_count; const auto byte_length = (samples - written) * sizeof(float) * this->channel_count;
if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length); if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
@ -34,10 +99,11 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
if(this->on_read) if(this->on_read)
this->on_read(); this->on_read();
return samples; return samples;
} else { } else {
const auto byte_length = available_samples * sizeof(float) * this->channel_count; const auto byte_length = available_samples * sizeof(float) * this->channel_count;
if(buffer)memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length); if(buffer) memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
this->buffer.advance_read_ptr(byte_length); this->buffer.advance_read_ptr(byte_length);
written += available_samples; written += available_samples;
written_bytes += byte_length; written_bytes += byte_length;
@ -48,14 +114,58 @@ ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
if(fn(samples - written)) if(fn(samples - written))
goto load_buffer; goto load_buffer;
if(buffer)memset(buffer, 0, (samples - written) * sizeof(float) * this->channel_count); if(buffer)
memset((char*) buffer + written_bytes, 0, (samples - written) * sizeof(float) * this->channel_count);
this->buffering = true; this->buffering = true;
this->fade_in_start = this->buffer.write_ptr();
log_trace(category::audio, tr("Start buffering due to underflow."), (void*) this->fade_in_start);
this->will_buffer_in = -1;
if(this->on_read) if(this->on_read)
this->on_read(); this->on_read();
return written; /* return the written samples */ return written; /* return the written samples */
} }
ssize_t AudioOutputSource::enqueue_silence(size_t samples) {
size_t enqueued{0};
auto free_bytes = this->buffer.free_count();
auto free_samples = free_bytes / sizeof(float) / this->channel_count;
if(this->max_buffered_samples && free_samples > this->max_buffered_samples) free_samples = this->max_buffered_samples;
if(free_samples >= samples) {
const auto byte_length = samples * sizeof(float) * this->channel_count;
memset(this->buffer.write_ptr(), 0, byte_length);
this->buffer.advance_write_ptr(byte_length);
return samples;
} else {
const auto byte_length = free_samples * sizeof(float) * this->channel_count;
memset(this->buffer.write_ptr(), 0, byte_length);
this->buffer.advance_write_ptr(byte_length);
enqueued += free_samples;
}
if(auto fn = this->on_overflow; fn)
fn(samples - enqueued);
switch (this->overflow_strategy) {
case overflow_strategy::discard_input:
return -2;
case overflow_strategy::discard_buffer_all:
this->buffer.clear();
break;
case overflow_strategy::discard_buffer_half:
this->buffer.advance_read_ptr(this->buffer.fill_count() / 2);
break;
case overflow_strategy::ignore:
break;
}
this->fade_in_start = this->buffer.write_ptr(); /* so we fade in from silence */
return enqueued;
}
ssize_t AudioOutputSource::enqueue_samples(const void *buffer, size_t samples) { ssize_t AudioOutputSource::enqueue_samples(const void *buffer, size_t samples) {
size_t enqueued{0}; size_t enqueued{0};

View File

@ -50,8 +50,12 @@ namespace tc::audio {
} }
bool buffering{true}; bool buffering{true};
char* fade_in_start{nullptr};
ssize_t will_buffer_in{-1};
size_t min_buffered_samples{0}; size_t min_buffered_samples{0};
size_t max_buffered_samples{0}; size_t max_buffered_samples{0};
size_t fadeout_sample_length{360};
overflow_strategy::value overflow_strategy = overflow_strategy::discard_buffer_half; overflow_strategy::value overflow_strategy = overflow_strategy::discard_buffer_half;
@ -62,14 +66,19 @@ namespace tc::audio {
void clear(); void clear();
ssize_t pop_samples(void* /* output buffer */, size_t /* sample count */); ssize_t pop_samples(void* /* output buffer */, size_t /* sample count */);
ssize_t enqueue_silence(size_t /* sample count */);
ssize_t enqueue_samples(const void * /* input buffer */, size_t /* sample count */); ssize_t enqueue_samples(const void * /* input buffer */, size_t /* sample count */);
ssize_t enqueue_samples_no_interleave(const void * /* input buffer */, size_t /* sample count */); ssize_t enqueue_samples_no_interleave(const void * /* input buffer */, size_t /* sample count */);
private: private:
AudioOutputSource(AudioOutput* handle, size_t channel_count, size_t sample_rate, ssize_t max_buffer_sample_count = -1) : AudioOutputSource(AudioOutput* handle, size_t channel_count, size_t sample_rate, ssize_t max_buffer_sample_count = -1) :
handle(handle), channel_count(channel_count), sample_rate(sample_rate), handle(handle), channel_count(channel_count), sample_rate(sample_rate),
buffer{max_buffer_sample_count == -1 ? channel_count * sample_rate * sizeof(float) : max_buffer_sample_count * channel_count * sizeof(float)} { buffer{max_buffer_sample_count == -1 ? channel_count * sample_rate * sizeof(float) : max_buffer_sample_count * channel_count * sizeof(float)} {
this->clear();
} }
void do_fade_out(size_t /* pop count */);
void do_fade_in();
tc::ring_buffer buffer; tc::ring_buffer buffer;
}; };

View File

@ -40,12 +40,12 @@ namespace tc {
/** /**
* @return number of bytes written on success * @return number of bytes written on success
*/ */
virtual ssize_t encode(std::string& /* error */, const void* /* source */, void* /* destination */, size_t /* destination byte length */) = 0; virtual ssize_t encode(std::string& /* error */, const void* /* source */, void* /* destination */, size_t /* destination byte length */, bool /* head package */) = 0;
/** /**
* @return number of samples on success * @return number of samples on success
*/ */
virtual ssize_t decode(std::string& /* error */, const void* /* source */, size_t /* source byte length */, void* /* destination */) = 0; virtual ssize_t decode(std::string& /* error */, const void* /* source */, size_t /* source byte length */, void* /* destination */, bool /* fec decoding */) = 0;
virtual ssize_t decode_lost(std::string& /* error */, size_t /* packets */) = 0; virtual ssize_t decode_lost(std::string& /* error */, size_t /* packets */) = 0;
virtual size_t expected_encoded_length(size_t /* sample count */) = 0; virtual size_t expected_encoded_length(size_t /* sample count */) = 0;

View File

@ -29,7 +29,7 @@ bool OpusConverter::initialize(std::string &error, int application_type) {
void OpusConverter::reset_encoder() { void OpusConverter::reset_encoder() {
lock_guard lock(this->coder_lock); lock_guard lock(this->coder_lock);
log_info(category::audio, tr("Resetting encoder"));
auto result = opus_encoder_ctl(this->encoder, OPUS_RESET_STATE); auto result = opus_encoder_ctl(this->encoder, OPUS_RESET_STATE);
if(result != OPUS_OK) if(result != OPUS_OK)
log_warn(category::audio, tr("Failed to reset opus encoder. Opus result: {}"), result); log_warn(category::audio, tr("Failed to reset opus encoder. Opus result: {}"), result);
@ -38,9 +38,11 @@ void OpusConverter::reset_encoder() {
void OpusConverter::reset_decoder() { void OpusConverter::reset_decoder() {
lock_guard lock(this->coder_lock); lock_guard lock(this->coder_lock);
log_info(category::audio, tr("Resetting decoder"));
auto result = opus_decoder_ctl(this->decoder, OPUS_RESET_STATE); auto result = opus_decoder_ctl(this->decoder, OPUS_RESET_STATE);
if(result != OPUS_OK) if(result != OPUS_OK)
log_warn(category::audio, tr("Failed to reset opus decoder. Opus result: {}"), result); log_warn(category::audio, tr("Failed to reset opus decoder. Opus result: {}"), result);
this->fec_decoder_ = true;
} }
@ -54,9 +56,10 @@ void OpusConverter::finalize() {
this->decoder = nullptr; this->decoder = nullptr;
} }
ssize_t OpusConverter::encode(std::string &error, const void *source, void *target, size_t target_length) { ssize_t OpusConverter::encode(std::string &error, const void *source, void *target, size_t target_length, bool head_package) {
lock_guard lock(this->coder_lock); lock_guard lock(this->coder_lock);
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(head_package ? 100 : 15));
auto result = opus_encode_float(this->encoder, (float*) source, (int) this->_frame_size, (uint8_t*) target, (opus_int32) target_length); auto result = opus_encode_float(this->encoder, (float*) source, (int) this->_frame_size, (uint8_t*) target, (opus_int32) target_length);
if(result < OPUS_OK) { if(result < OPUS_OK) {
error = to_string(result) + "|" + opus_strerror(result); error = to_string(result) + "|" + opus_strerror(result);
@ -65,10 +68,10 @@ ssize_t OpusConverter::encode(std::string &error, const void *source, void *targ
return result; return result;
} }
ssize_t OpusConverter::decode(std::string &error, const void *source, size_t source_length, void *target) { ssize_t OpusConverter::decode(std::string &error, const void *source, size_t source_length, void *target, bool use_fec) {
lock_guard lock(this->coder_lock); lock_guard lock(this->coder_lock);
auto result = opus_decode_float(this->decoder, (uint8_t*) source, (opus_int32) source_length, (float*) target, (int) this->_frame_size, 0); auto result = opus_decode_float(this->decoder, (uint8_t*) source, (opus_int32) source_length, (float*) target, (int) this->_frame_size, use_fec ? 1 : 0);
if(result < OPUS_OK) { if(result < OPUS_OK) {
error = to_string(result) + "|" + opus_strerror(result); error = to_string(result) + "|" + opus_strerror(result);
return -1; return -1;
@ -85,6 +88,7 @@ ssize_t OpusConverter::decode_lost(std::string &error, size_t packets) {
if(result < OPUS_OK) if(result < OPUS_OK)
log_warn(category::audio, tr("Opus decode lost resulted in error: {}"), result); log_warn(category::audio, tr("Opus decode lost resulted in error: {}"), result);
} }
this->fec_decoder_ = true;
free(buffer); free(buffer);
return 0; return 0;
} }
@ -123,6 +127,19 @@ bool OpusConverter::_initialize_encoder(std::string &error) {
error = "failed to set bitrate (" + to_string(error_id) + ")"; error = "failed to set bitrate (" + to_string(error_id) + ")";
return false; return false;
} }
error_id = opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
if(error_id) {
error = "failed to enable fec (" + to_string(error_id) + ")";
return false;
}
error_id = opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(15));
if(error_id) {
error = "failed to assume a 15% packet loss (" + to_string(error_id) + ")";
return false;
}
return true; return true;
} }

View File

@ -20,8 +20,8 @@ namespace tc {
void reset_encoder() override; void reset_encoder() override;
void reset_decoder() override; void reset_decoder() override;
ssize_t encode(std::string & /* error */, const void * /* source */, void * /* target */, size_t /* target size */) override; ssize_t encode(std::string & /* error */, const void * /* source */, void * /* target */, size_t /* target size */, bool /* head package */) override;
ssize_t decode(std::string & /* error */, const void * /* source */, size_t /* source size */, void *pVoid1) override; ssize_t decode(std::string & /* error */, const void * /* source */, size_t /* source size */, void *pVoid1, bool /* use fec */) override;
ssize_t decode_lost(std::string &string, size_t /* packets */) override; ssize_t decode_lost(std::string &string, size_t /* packets */) override;
@ -31,6 +31,7 @@ namespace tc {
OpusDecoder* decoder = nullptr; OpusDecoder* decoder = nullptr;
OpusEncoder* encoder = nullptr; OpusEncoder* encoder = nullptr;
bool fec_decoder_{false};
int _application_type = 0; int _application_type = 0;
bool _finalize_encoder(std::string& /* error */); bool _finalize_encoder(std::string& /* error */);

View File

@ -211,7 +211,7 @@ namespace tc::audio::sounds {
if(current_size > max_size) return false; if(current_size > max_size) return false;
const auto size_left = max_size - current_size; const auto size_left = max_size - current_size;
return size_left >= this->cache_buffer_sample_size(); return size_left >= this->cache_buffer_sample_size() * 1.5; /* ensure we've a bit more space */
} }
}; };

View File

@ -164,8 +164,13 @@ void VoiceSender::encode_raw_frame(const std::unique_ptr<AudioFrame> &frame) {
return; return;
} }
if(codec_data) if(codec_data) {
codec_data->packet_counter = 0; codec_data->packet_counter = 0;
if(auto converter = codec_data->converter; converter) {
log_trace(category::voice_connection, tr("Resetting encoder"));
converter->reset_encoder();
}
}
auto server = this->handle->handle(); auto server = this->handle->handle();
server->send_voice_data(this->_buffer, 0, codec, flag_head); server->send_voice_data(this->_buffer, 0, codec, flag_head);
return; return;
@ -205,7 +210,7 @@ void VoiceSender::encode_raw_frame(const std::unique_ptr<AudioFrame> &frame) {
} }
char _packet_buffer[512]; char _packet_buffer[512];
auto encoded_bytes = codec_data->converter->encode(error, this->_buffer, _packet_buffer, 512); auto encoded_bytes = codec_data->converter->encode(error, this->_buffer, _packet_buffer, 512, flag_head);
if(encoded_bytes <= 0) { if(encoded_bytes <= 0) {
log_error(category::voice_connection, tr("Failed to encode voice: {}"), error); log_error(category::voice_connection, tr("Failed to encode voice: {}"), error);
return; return;

View File

@ -321,6 +321,12 @@ inline constexpr uint16_t packet_id_diff(uint16_t lower, uint16_t upper) {
#define MAX_LOST_PACKETS (6) #define MAX_LOST_PACKETS (6)
#define target_buffer_length 16384 #define target_buffer_length 16384
void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& buffer, codec::value codec, bool is_head) { void VoiceClient::process_packet(uint16_t packet_id, const pipes::buffer_view& buffer, codec::value codec, bool is_head) {
#if 0
if(rand() % 10 == 0) {
log_info(category::audio, tr("Dropping audio packet id {}"), packet_id);
return;
}
#endif
if(codec < 0 || codec > this->codec.size()) { if(codec < 0 || codec > this->codec.size()) {
log_warn(category::voice_connection, tr("Received voice packet from client {} with unknown codec ({})"), this->_client_id, codec); log_warn(category::voice_connection, tr("Received voice packet from client {} with unknown codec ({})"), this->_client_id, codec);
return; return;
@ -488,7 +494,7 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
replay_head = audio_codec.pending_buffers; replay_head = audio_codec.pending_buffers;
audio_codec.pending_buffers = skip_ptr[SKIP_SEQ_LENGTH]; audio_codec.pending_buffers = skip_ptr[SKIP_SEQ_LENGTH];
skip_ptr[SKIP_SEQ_LENGTH - 1]->next = nullptr; skip_ptr[SKIP_SEQ_LENGTH - 1]->next = nullptr;
log_trace(category::voice_connection, tr("Skipping from {} to {} because of {} packets in a row"), audio_codec.last_packet_id, head->packet_id, SKIP_SEQ_LENGTH); log_trace(category::voice_connection, tr("Skipping from {} to {} because of {} packets in a row"), audio_codec.last_packet_id, replay_head->packet_id, SKIP_SEQ_LENGTH);
/* Do not set process_pending to false, because we're not done /* Do not set process_pending to false, because we're not done
* We're just replaying all loose packets which are not within a sequence until we reach a sequence * We're just replaying all loose packets which are not within a sequence until we reach a sequence
@ -508,7 +514,7 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
audio_codec.pending_buffers = head->next; audio_codec.pending_buffers = head->next;
head->next = nullptr; head->next = nullptr;
log_trace(category::voice_connection, tr("Skipping from {} to {} because of over 6 packets between"), log_trace(category::voice_connection, tr("Skipping from {} to {} because of over 6 packets between"),
audio_codec.last_packet_id, head->packet_id); audio_codec.last_packet_id, replay_head->packet_id);
/* do not negate process_pending here. Same reason as with the 3 sequence */ /* do not negate process_pending here. Same reason as with the 3 sequence */
} else { } else {
/* no packets we're willing to replay */ /* no packets we're willing to replay */
@ -542,27 +548,60 @@ void VoiceClient::event_execute(const std::chrono::system_clock::time_point &sch
if(replay_head->buffer.empty()) { if(replay_head->buffer.empty()) {
this->set_state(state::stopping); this->set_state(state::stopping);
log_debug(category::voice_connection, tr("Client {} send a stop signal. Flushing stream and stopping"), this->_client_id); log_debug(category::voice_connection, tr("Client {} send a stop signal. Flushing stream and stopping"), this->_client_id);
} else if(local_last_pid > replay_head->packet_id) {
log_debug(category::voice_connection, tr("Client {} tried to replay too old voice chunk. Current id is {}, attempted chunk id is {}. Dropping buffer."), this->_client_id, local_last_pid, replay_head->packet_id);
} else { } else {
auto lost_packets = packet_id_diff(local_last_pid, replay_head->packet_id) - 1; auto lost_packets = packet_id_diff(local_last_pid, replay_head->packet_id) - 1;
if(lost_packets > 6) { if(lost_packets > 10) {
log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream ({} to {}). Resetting decoder."), this->_client_id, lost_packets, local_last_pid, replay_head->packet_id); log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream ({} to {}). Resetting decoder."), this->_client_id, lost_packets, local_last_pid, replay_head->packet_id);
replay_head->reset_decoder = true; replay_head->reset_decoder = true;
} else if(lost_packets > 0) { } else if(lost_packets > 0) {
log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream. Skipping ahead."), this->_client_id, lost_packets); log_debug(category::voice_connection, tr("Client {} seems to be missing {} packets in stream. FEC decoding it."), this->_client_id, lost_packets);
/*
if(audio_codec.converter->decode_lost(error, lost_packets)) if(audio_codec.converter->decode_lost(error, lost_packets))
log_warn(category::audio, tr("Failed to decode lost packets for client {}: {}"), this->_client_id, error); log_warn(category::audio, tr("Failed to decode lost packets for client {}: {}"), this->_client_id, error);
*/
auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, true);
if(decoded)
this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size);
} }
if(replay_head->reset_decoder) const auto is_new_audio_stream = this->_state != state::buffering && this->_state != state::playing;
audio_codec.converter->reset_decoder(); if(replay_head->reset_decoder || is_new_audio_stream) {
audio_codec.converter->reset_decoder();
replay_head->reset_decoder = false;
#if 1 /* Better approch */
/* initialize with last packet */
char target_buffer[target_buffer_length];
if(target_buffer_length > audio_codec.converter->expected_decoded_length(replay_head->buffer.data_ptr(), replay_head->buffer.length())) {
audio_codec.converter->decode(error, replay_head->buffer.data_ptr(), replay_head->buffer.length(), target_buffer, 1);
} else {
//TODO: May a small warning here?
}
#endif
}
#if 0 /* (maybe) TS3 approch */
if(replay_head->head) {
/* initialize with last packet */
char target_buffer[target_buffer_length];
if(target_buffer_length > audio_codec.converter->expected_decoded_length(replay_head->buffer.data_ptr(), replay_head->buffer.length())) {
audio_codec.converter->decode(error, replay_head->buffer.data_ptr(), replay_head->buffer.length(), target_buffer, 1);
} else {
//TODO: May a small warning here?
}
}
#endif
//TODO: Use statically allocated buffer? //TODO: Use statically allocated buffer?
auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer); auto decoded = this->decode_buffer(audio_codec.codec, replay_head->buffer, false);
if(!decoded) { if(!decoded) {
log_warn(category::audio, tr("Failed to decode buffer for client {} (nullptr). Dropping buffer."), this->_client_id, error); log_warn(category::audio, tr("Failed to decode buffer for client {} (nullptr). Dropping buffer."), this->_client_id, error);
} else { } else {
if(is_new_audio_stream) {
log_warn(category::audio, tr("New audio chunk for client {}"), this->_client_id);
//this->output_source->enqueue_silence((size_t) ceil(0.0075f * (float) this->output_source->sample_rate)); /* enqueue 7.5ms silence so we give the next packet a chance to be send */
}
this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size); this->output_source->enqueue_samples(decoded->sample_data, decoded->sample_size);
this->set_state(state::playing); this->set_state(state::playing);
} }
@ -613,7 +652,7 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) {
return; return;
} }
codec_data.resampler = make_shared<audio::AudioResampler>(codec_data.converter->sample_rate(), this->output_source->sample_rate, codec_data.converter->channels()); codec_data.resampler = make_shared<audio::AudioResampler>(codec_data.converter->sample_rate(), this->output_source->sample_rate, this->output_source->channel_count);
if(!codec_data.resampler->valid()) { if(!codec_data.resampler->valid()) {
log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize resampler"), audio_codec, this->_client_id); log_warn(category::voice_connection, tr("Failed to initialized codec {} for client {}. Failed to initialize resampler"), audio_codec, this->_client_id);
return; return;
@ -623,7 +662,7 @@ void VoiceClient::initialize_code(const codec::value &audio_codec) {
log_trace(category::voice_connection, tr("Successfully initialized codec {} for client {}."), audio_codec, this->_client_id); log_trace(category::voice_connection, tr("Successfully initialized codec {} for client {}."), audio_codec, this->_client_id);
} }
std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer) { std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::value &audio_codec, const pipes::buffer_view &buffer, bool fec) {
assert(this->output_source); assert(this->output_source);
auto& codec_data = this->codec[audio_codec]; auto& codec_data = this->codec[audio_codec];
@ -638,13 +677,19 @@ std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::val
log_warn(category::voice_connection, tr("Failed to decode audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, codec_data.converter->expected_decoded_length(buffer.data_ptr(), buffer.length())); log_warn(category::voice_connection, tr("Failed to decode audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, codec_data.converter->expected_decoded_length(buffer.data_ptr(), buffer.length()));
return nullptr; return nullptr;
} }
auto samples = codec_data.converter->decode(error, buffer.data_ptr(), buffer.length(), target_buffer); auto samples = codec_data.converter->decode(error, buffer.data_ptr(), buffer.length(), target_buffer, fec);
if(samples < 0) { if(samples < 0) {
log_warn(category::voice_connection, tr("Failed to decode audio data: {}"), error); log_warn(category::voice_connection, tr("Failed to decode audio data: {}"), error);
return nullptr; return nullptr;
} }
if(target_buffer_length < codec_data.resampler->estimated_output_size(samples) * codec_data.resampler->channels() * 4) {
log_warn(category::voice_connection, tr("Failed to resample audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, (codec_data.resampler->estimated_output_size(samples) * codec_data.resampler->channels() * 4)); if(!audio::merge::merge_channels_interleaved(target_buffer, this->output_source->channel_count, target_buffer, codec_data.converter->channels(), samples)) {
log_warn(category::voice_connection, tr("Failed to merge channels to output stream channel count!"));
return nullptr;
}
if(target_buffer_length < codec_data.resampler->estimated_output_size(samples) * this->output_source->channel_count * sizeof(float)) {
log_warn(category::voice_connection, tr("Failed to resample audio data. Target buffer is smaller then expected bytes ({} < {})"), target_buffer_length, (codec_data.resampler->estimated_output_size(samples) * this->output_source->channel_count * 4));
return nullptr; return nullptr;
} }
@ -654,11 +699,6 @@ std::shared_ptr<audio::SampleBuffer> VoiceClient::decode_buffer(const codec::val
return nullptr; return nullptr;
} }
if(!audio::merge::merge_channels_interleaved(target_buffer, this->output_source->channel_count, target_buffer, codec_data.resampler->channels(), resampled_samples)) {
log_warn(category::voice_connection, tr("Failed to merge channels to output stream channel count!"));
return nullptr;
}
if(this->_volume != 1) { if(this->_volume != 1) {
auto buf = (float*) target_buffer; auto buf = (float*) target_buffer;
auto count = this->output_source->channel_count * resampled_samples; auto count = this->output_source->channel_count * resampled_samples;

View File

@ -164,7 +164,7 @@ namespace tc::connection {
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override; void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
/* its recommend to call this in correct packet oder */ /* its recommend to call this in correct packet oder */
std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */); std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */, bool /* fec */);
}; };

View File

@ -238,6 +238,11 @@ namespace tc {
assert(this->fill_count() >= 0); assert(this->fill_count() >= 0);
} }
char* ring_buffer::calculate_advanced_write_ptr(size_t bytes) {
auto offset{this->write_offset.load() + bytes};
return this->memory.address + (offset % this->memory.capacity);
}
const void* ring_buffer::read_ptr() const { const void* ring_buffer::read_ptr() const {
auto offset{this->read_offset.load()}; auto offset{this->read_offset.load()};
return this->memory.address + (offset % this->memory.capacity); return this->memory.address + (offset % this->memory.capacity);

View File

@ -18,6 +18,8 @@ namespace tc {
char* write_ptr(); char* write_ptr();
void advance_write_ptr(size_t /* count */); void advance_write_ptr(size_t /* count */);
char* calculate_advanced_write_ptr(size_t /* count */bytes);
/* do not read more than the capacity! */ /* do not read more than the capacity! */
[[nodiscard]] const void* read_ptr() const; [[nodiscard]] const void* read_ptr() const;
void advance_read_ptr(size_t /* count */); void advance_read_ptr(size_t /* count */);

View File

@ -620,3 +620,14 @@
[59B2][2] Rollback done [59B2][2] Rollback done
[59B2][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno= [59B2][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[59B2][2] ----------- log ended at 2020-04-22.20:01:11 ----------- [59B2][2] ----------- log ended at 2020-04-22.20:01:11 -----------
[5EA7][2] ----------- log started at 2020-04-22.20:01:37 -----------
[5EA7][2] App executed as admin: no
[5EA7][2] loading config from file updater\test\dummy_config.json
[5EA7][1] Loaded 1 locking actions and 1 moving actions
[5EA7][2] Awaiting the unlocking of all files
[5EA7][5] Failed to lock file D:\Program Files\IDA 7.0\ida.exe. Timeout: 5000, Time tried: 70663
[5EA7][2] Rollbacking 0 moved files
[5EA7][2] Rollbacking 0 deleted files
[5EA7][2] Rollback done
[5EA7][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6U0dWc2JHOGdWMjl5YkdRZ1pYSnliM0k9O2Vycm9yX21lc3NhZ2U6Wm1GcGJHVmtJSFJ2SUd4dlkyc2dabWxzWlE9PQ==
[5EA7][2] ----------- log ended at 2020-04-22.20:02:48 -----------

View File

@ -1,6 +1,6 @@
{ {
"name": "TeaClient", "name": "TeaClient",
"version": "1.4.5-3", "version": "1.4.6",
"description": "", "description": "",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {