Browse Source

Drive desktop call clock from rendered PCM and capture timing

stream-multiview
evgeny 1 week ago
parent
commit
39bfedca86
  1. 4
      src/call/call_audio.c
  2. 26
      tools/chatgui/src/voice_audio_io.cpp
  3. 4
      tools/chatgui/src/voice_audio_io.h

4
src/call/call_audio.c

@ -492,7 +492,9 @@ int call_audio_pull_pcm(uint64_t call_id, int16_t* out, int max_samples) {
}
if (call_tones_ended_pending(tones))
n = call_tones_render(tones, out, max_samples);
call_jitter_silence(j, (unsigned)n);
memset(out + n, 0, (size_t)(max_samples - n) * sizeof(*out));
call_jitter_silence(j, (unsigned)max_samples);
n = max_samples;
} else {
n = call_jitter_pull(j, out, max_samples);
int previous_gap = g_gap_samples;

26
tools/chatgui/src/voice_audio_io.cpp

@ -122,7 +122,7 @@ void VoiceAudioIo::reference(const float* pcm, unsigned frames, int64_t firstTim
}
/* Выход всегда обслуживается независимо от capture/VAD. Уже потреблённый PCM становится референсом у владельца микшера. */
void VoiceAudioIo::mix(float* stereo, unsigned frames, int64_t firstTimeUs) {
void VoiceAudioIo::mix(float* stereo, unsigned frames, int64_t firstTimeUs, bool measured) {
uint64_t epoch = m_playEpoch.load();
if (!firstTimeUs) firstTimeUs = voice_time_us();
unsigned consumed = 0;
@ -140,9 +140,9 @@ void VoiceAudioIo::mix(float* stereo, unsigned frames, int64_t firstTimeUs) {
++m_playPosition;
++consumed;
}
if (m_kind == Kind::Radio && consumed) {
if (consumed) {
PlaybackProgress progress{epoch, m_playCurrent.position + m_playPosition,
firstTimeUs + (int64_t)consumed * 1000000 / 48000};
firstTimeUs + (int64_t)consumed * 1000000 / 48000, measured};
if (!m_playProgress.push(progress)) m_progressDropped.fetch_add(1, std::memory_order_relaxed);
}
m_wake.notify_all();
@ -151,11 +151,18 @@ void VoiceAudioIo::mix(float* stereo, unsigned frames, int64_t firstTimeUs) {
void VoiceAudioIo::playbackLoop() {
uint64_t epoch = m_playEpoch.load();
uint64_t position = 0;
call_clock_snapshot clock{};
if (m_kind == Kind::Call && call_audio_get_clock(m_session, voice_time_us(), &clock) != 0)
DEBUG_WARN(DEBUG_CATEGORY_CALL, "voice-io: call clock unavailable session=%llu", (unsigned long long)m_session);
if (m_kind == Kind::Radio) radio_audio_playback_progress(m_session, 0, 0);
while (m_running) {
uint64_t requested = m_playEpoch.load();
if (requested != epoch) {
if (m_kind == Kind::Call) call_audio_reset_io(m_session, 1);
if (m_kind == Kind::Call) {
call_audio_reset_io(m_session, 1);
if (call_audio_get_clock(m_session, voice_time_us(), &clock) != 0)
DEBUG_WARN(DEBUG_CATEGORY_CALL, "voice-io: reset call clock unavailable session=%llu", (unsigned long long)m_session);
}
else radio_audio_reset_playback(m_session);
position = 0;
epoch = requested;
@ -163,11 +170,13 @@ void VoiceAudioIo::playbackLoop() {
PlaybackProgress progress{};
for (unsigned count = 0; count < 32 && m_playProgress.pop(progress); ++count) {
if (progress.epoch != epoch) continue;
int64_t remaining = progress.endTimeUs - voice_time_us();
radio_audio_playback_progress(m_session, progress.position, remaining > 0 ? (remaining + 99) / 100 : 0);
int64_t now = voice_time_us(), remaining = progress.endTimeUs - now;
if (m_kind == Kind::Call)
call_audio_playback_progress(m_session, clock.epoch, progress.position, progress.endTimeUs, now, progress.measured);
else radio_audio_playback_progress(m_session, progress.position, remaining > 0 ? (remaining + 99) / 100 : 0);
}
unsigned lost = m_progressDropped.exchange(0);
if (lost) DEBUG_WARN(DEBUG_CATEGORY_RADIO, "voice-io: playback progress overflow session=%llu dropped=%u",
if (lost) DEBUG_WARN(DEBUG_CATEGORY_CALL, "voice-io: playback progress overflow session=%llu dropped=%u",
(unsigned long long)m_session, lost);
if (m_playback.size() < 2) {
Playback block{}; block.epoch = epoch; block.position = position;
@ -282,7 +291,7 @@ void VoiceAudioIo::captureLoop() {
if (mark < markerCount) applyMarker(markers[mark].kind, markers[mark].value);
begin = end;
}
if (m_kind == Kind::Call && frames == 960) call_audio_feed_prepared_pcm(m_session, cleaned, muteMask);
if (m_kind == Kind::Call && frames == 960) call_audio_feed_prepared_pcm(m_session, cleaned, muteMask, frameTime);
filled = 0; markerCount = 0;
};
// Отчёт работает и при отсутствии PCM, иначе длительный провал захвата невидим.
@ -350,6 +359,7 @@ void VoiceAudioIo::captureLoop() {
filled = 0; markerCount = 0;
if (aec) speex_aec_reset(aec);
if (m_kind == Kind::Radio) radio_audio_capture_discontinuity(m_session);
else call_audio_reset_io(m_session, 0);
}
expectedSequence = event.sequence + event.frames;
captureRoute = event.route;

4
tools/chatgui/src/voice_audio_io.h

@ -27,7 +27,7 @@ public:
/* firstTimeUs — монотонное время первого физического PCM-фрейма (backend либо оценка). */
void capture(const int16_t* pcm, unsigned frames, int64_t firstTimeUs, uint64_t route = 0, bool missing = false);
void reference(const float* pcm, unsigned frames, int64_t firstTimeUs);
void mix(float* stereo, unsigned frames, int64_t firstTimeUs = 0);
void mix(float* stereo, unsigned frames, int64_t firstTimeUs = 0, bool measured = false);
private:
enum class EventKind { Pcm, Ptt, Mute, Aec, Stop };
struct Event {
@ -41,7 +41,7 @@ private:
int16_t pcm[1920]{};
};
struct Playback { uint64_t epoch = 0, position = 0; int16_t pcm[1920]{}; };
struct PlaybackProgress { uint64_t epoch = 0, position = 0; int64_t endTimeUs = 0; };
struct PlaybackProgress { uint64_t epoch = 0, position = 0; int64_t endTimeUs = 0; bool measured = false; };
AudioEventQueue<Event, 32> m_captureEvents, m_references;
AudioEventQueue<Playback, 12> m_playback;
AudioEventQueue<PlaybackProgress, 32> m_playProgress;

Loading…
Cancel
Save