#include "radio_audio_engine.h" #include "sound_manager.h" #include "../transport/gui_bridge.h" #include "miniaudio.h" extern "C" { #include "radio/radio_audio.h" #include "../../lib/debug_config.h" } #include static RadioAudioEngine* g_radioAudioEngine = nullptr; static void radioAudioDataCallback(ma_device* dev, void* out, const void* in, ma_uint32 frames) { RadioAudioEngine* e = (RadioAudioEngine*)dev->pUserData; if (e) e->dataCallback(in, out, frames); } RadioAudioEngine* RadioAudioEngine::instance() { if (!g_radioAudioEngine) g_radioAudioEngine = new RadioAudioEngine(); return g_radioAudioEngine; } int RadioAudioEngine::start(quint64 groupId) { if (m_active) { if (m_groupId == groupId) return 0; stop(); } m_groupId = groupId; struct UTUN_INSTANCE* inst = gui_bridge_get_inst(); if (!inst) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "RadioAudio: no instance, cannot start"); return -1; } if (radio_audio_init(inst) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "RadioAudio: radio_audio_init failed"); return -1; } if (radio_audio_start(groupId) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "RadioAudio: radio_audio_start failed grp=%016llx", (unsigned long long)groupId); return -1; } m_captureBuf.assign(RADIO_AUDIO_FRAME_SAMPLES, 0); m_capturePos = 0; if (initDevice() != 0) { radio_audio_stop(); return -1; } m_active = true; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RadioAudio: started grp=%016llx", (unsigned long long)groupId); return 0; } int RadioAudioEngine::initDevice() { ma_context* ctx = SoundManager::instance()->context(); if (!ctx) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "RadioAudio: no audio context"); return -1; } m_device = new ma_device; std::memset(m_device, 0, sizeof(*m_device)); ma_device_config config = ma_device_config_init(ma_device_type_duplex); config.capture.format = ma_format_s16; config.capture.channels = 1; config.playback.format = ma_format_s16; config.playback.channels = 1; config.sampleRate = RADIO_AUDIO_SAMPLE_RATE; config.periodSizeInFrames = 480; config.dataCallback = radioAudioDataCallback; config.pUserData = this; if (ma_device_init(ctx, &config, m_device) != MA_SUCCESS) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "RadioAudio: device init failed, trying default"); config.playback.pDeviceID = nullptr; config.capture.pDeviceID = nullptr; if (ma_device_init(ctx, &config, m_device) != MA_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "RadioAudio: default device init also failed"); delete m_device; m_device = nullptr; return -1; } } if (ma_device_start(m_device) != MA_SUCCESS) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "RadioAudio: device start failed"); ma_device_uninit(m_device); delete m_device; m_device = nullptr; return -1; } m_deviceStarted = true; m_capturePos = 0; return 0; } void RadioAudioEngine::stop() { if (!m_active && !m_device && !m_deviceStarted) return; m_active = false; m_talking = false; quint64 gid = m_groupId.load(); teardownDevice(); radio_audio_stop(); m_groupId = 0; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RadioAudio: stopped"); } void RadioAudioEngine::talkBegin() { if (!m_active) return; m_talking = true; radio_audio_talk_begin(m_groupId.load()); } void RadioAudioEngine::talkEnd() { if (!m_active) return; m_talking = false; radio_audio_talk_end(m_groupId.load()); } void RadioAudioEngine::teardownDevice() { if (m_deviceStarted) { ma_device_stop(m_device); ma_device_uninit(m_device); m_deviceStarted = false; } if (m_device) { delete m_device; m_device = nullptr; } } void RadioAudioEngine::dataCallback(const void* in, void* out, unsigned int frames) { const int16_t* in16 = (const int16_t*)in; int16_t* out16 = (int16_t*)out; /* захват: копим 960 samples → encode+send (сервисный слой). Только когда PTT зажата. */ if (in16 && m_talking.load()) { for (unsigned int i = 0; i < frames; i++) { m_captureBuf[m_capturePos++] = in16[i]; if (m_capturePos >= (int)m_captureBuf.size()) { radio_audio_feed_pcm(m_groupId.load(), m_captureBuf.data(), (int)m_captureBuf.size()); m_capturePos = 0; } } } /* воспроизведение: микшированный PCM из сервисного слоя */ int n = radio_audio_pull_pcm(m_groupId.load(), out16, (int)frames); if (n < (int)frames) std::memset(out16 + n, 0, (size_t)(frames - n) * sizeof(int16_t)); }