|
|
|
|
@ -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; |
|
|
|
|
|