#include "RkAudio.h" #include "BoostLog.h" #include #include namespace RkAudio { constexpr RK_U32 VqeFrameSample = 256; // 16ms; constexpr auto AudioNode = "hw:0,0"; constexpr auto ParamFilePath = "/system/etc/RKAP_3A_Para.bin"; static SAMPLE_FORMAT_E rkAiFormat(Format::SampleType sampleType) { SAMPLE_FORMAT_E ret = RK_SAMPLE_FMT_NONE; switch (sampleType) { case Format::SampleType::Unknown: ret = RK_SAMPLE_FMT_NONE; break; case Format::SampleType::SignedInt16: ret = RK_SAMPLE_FMT_S16; break; case Format::SampleType::SignedInt: ret = RK_SAMPLE_FMT_S32; break; case Format::SampleType::Float: ret = RK_SAMPLE_FMT_FLT; break; default: LOG(error) << "unkonwn sample type: " << static_cast(sampleType); ret = RK_SAMPLE_FMT_NONE; break; } return ret; } Input::Input() { } Input::~Input() { if (m_channel >= 0) { stop(); } } bool Input::open(const Format &format, bool enableVqe) { bool ret = false; m_channel = 0; AI_CHN_ATTR_S parameter = {0}; parameter.pcAudioNode = (RK_CHAR *)AudioNode; parameter.enAiLayout = AI_LAYOUT_MIC_REF; // remove ref channel, and output mic mono parameter.enSampleFormat = rkAiFormat(format.sampleType); parameter.u32Channels = format.channels; parameter.u32SampleRate = format.sampleRate; parameter.u32NbSamples = format.sampleRate / 1000 * format.period; int status = RK_MPI_AI_SetChnAttr(m_channel, ¶meter); if (status) { LOG(error) << "RK_MPI_AI_SetChnAttr() failed, status: " << status; return ret; } status = RK_MPI_AI_EnableChn(m_channel); if (status) { LOG(error) << "RK_MPI_AI_EnableChn() failed, status: " << status; return ret; } if (enableVqe) { AI_TALKVQE_CONFIG_S config = {0}; status = RK_MPI_AI_GetTalkVqeAttr(m_channel, &config); if (status) { LOG(error) << "RK_MPI_AI_GetTalkVqeAttr() failed, status: " << status; return ret; } LOG(info) << "param file: " << config.aParamFilePath; config.s32WorkSampleRate = format.sampleRate; config.s32FrameSample = VqeFrameSample; config.u32OpenMask = AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; strncpy(config.aParamFilePath, ParamFilePath, sizeof(config.aParamFilePath)); RK_MPI_AI_SetTalkVqeAttr(m_channel, &config); if (status) { LOG(error) << "RK_MPI_AI_SetTalkVqeAttr() failed, status: " << status; return ret; } status = RK_MPI_AI_EnableVqe(m_channel); if (status) { LOG(error) << "RK_MPI_AI_EnableVqe() failed, status: " << status; return ret; } } status = RK_MPI_AI_StartStream(0); if (status) { LOG(info) << "start AI failed, status: " << status; return ret; } m_exit = false; m_thread = std::thread(&Input::run, this); ret = true; return ret; } void Input::stop() { m_exit = true; if (m_thread.joinable()) m_thread.join(); if (m_channel >= 0) { RK_MPI_AI_DisableVqe(m_channel); RK_MPI_AI_DisableChn(m_channel); m_channel = -1; } } void Input::setDataCallback(const ReadCallback &callback) { m_callback = callback; } void Input::run() { while (!m_exit) { auto mediaBuffer = RK_MPI_SYS_GetMediaBuffer(RK_ID_AI, 0, -1); if (!mediaBuffer) { LOG(error) << "RK_MPI_SYS_GetMediaBuffer() failed."; continue; } if (m_callback) { Frame frame; frame.data = reinterpret_cast(RK_MPI_MB_GetPtr(mediaBuffer)); frame.byteSize = RK_MPI_MB_GetSize(mediaBuffer); frame.frameSize = frame.byteSize / m_format.channels / sizeof(uint16_t); frame.timestamp = std::chrono::system_clock::now(); m_callback(frame); } RK_MPI_MB_ReleaseBuffer(mediaBuffer); } } Output::Output() { } Output::~Output() { close(); } bool Output::open(uint32_t sampleSize, uint32_t sampleRate, uint32_t channels, uint32_t period, bool enableVqe) { m_channel = 0; AO_CHN_ATTR_S parameter = {0}; parameter.pcAudioNode = (RK_CHAR *)AudioNode; parameter.enSampleFormat = RK_SAMPLE_FMT_S16; parameter.u32NbSamples = sampleRate / 1000 * period; parameter.u32SampleRate = sampleRate; parameter.u32Channels = channels; RK_MPI_AO_SetChnAttr(m_channel, ¶meter); auto status = RK_MPI_AO_EnableChn(m_channel); if (status != 0) { LOG(error) << "RK_MPI_AO_EnableChn() failed, status: " << status; return false; } if (enableVqe) { AO_VQE_CONFIG_S config = {0}; config.s32WorkSampleRate = sampleRate; config.s32FrameSample = VqeFrameSample; config.u32OpenMask = AO_VQE_MASK_ANR | AO_VQE_MASK_AGC; strncpy(config.aParamFilePath, ParamFilePath, sizeof(config.aParamFilePath)); RK_MPI_AO_SetVqeAttr(m_channel, &config); RK_MPI_AO_EnableVqe(m_channel); } return true; } void Output::close() { if (m_channel >= 0) { RK_MPI_AO_DisableVqe(m_channel); RK_MPI_AO_DisableChn(m_channel); m_channel = -1; } } void Output::write(const uint8_t *data, uint32_t byteSize) { if (m_channel < 0) return; auto buffer = RK_MPI_MB_CreateAudioBuffer(byteSize, RK_FALSE); if (buffer != nullptr) { memcpy(RK_MPI_MB_GetPtr(buffer), data, byteSize); RK_MPI_MB_SetSize(buffer, byteSize); RK_MPI_SYS_SendMediaBuffer(RK_ID_AO, m_channel, buffer); RK_MPI_MB_ReleaseBuffer(buffer); } else { LOG(error) << "RK_MPI_MB_CreateAudioBuffer() failed."; } } } // namespace RkAudio PcmStreamBuffer::PcmStreamBuffer(uint32_t sampleRate, uint32_t channels, RkAudio::Format::SampleType sampleType, uint32_t popDuration, uint32_t capacity) : m_sampleRate(sampleRate), m_channels(channels), m_buffer(capacity), m_capacity(capacity) { if (sampleType == RkAudio::Format::SignedInt16) { m_pointByteSize = 2; } uint32_t frameSize = sampleRate * channels * m_pointByteSize * popDuration / 1000; m_popBuffer = std::vector(frameSize); m_popFrame.data = m_popBuffer.data(); } bool PcmStreamBuffer::push(const RkAudio::Frame &frame) { std::lock_guard locker(m_mutex); uint32_t byteSize = availableByteSize(); uint32_t freeSize = m_capacity - byteSize; if (freeSize < frame.byteSize) { LOG_FORMAT(warning, "buffer is full, capacity: %d, free size: %d, need size: %d", m_capacity, freeSize, frame.byteSize); return false; } if ((m_tail + frame.byteSize) > m_capacity) { uint32_t size1 = m_capacity - m_tail; memcpy(m_buffer.data() + m_tail, frame.data, size1); uint32_t size2 = frame.byteSize - size1; memcpy(m_buffer.data(), frame.data + size1, size2); } else { memcpy(m_buffer.data() + m_tail, frame.data, frame.byteSize); } m_tail = (m_tail + frame.byteSize) % m_capacity; m_full = (m_tail == m_head); return true; } std::chrono::milliseconds PcmStreamBuffer::availableDuration() const { auto byteSize = availableByteSize(); return std::chrono::milliseconds(1000 * byteSize / (m_sampleRate * m_channels * m_pointByteSize)); } const RkAudio::Frame *PcmStreamBuffer::pop() { // return nullptr; std::lock_guard locker(m_mutex); auto byteSize = availableByteSize(); if (byteSize < m_popBuffer.size()) return nullptr; if ((m_head + m_popBuffer.size()) > m_capacity) { uint32_t size1 = m_capacity - m_head; memcpy(m_popBuffer.data(), m_buffer.data() + m_head, size1); uint32_t size2 = m_popBuffer.size() - size1; memcpy(m_popBuffer.data() + size1, m_buffer.data(), size2); } else { memcpy(m_popBuffer.data(), m_buffer.data() + m_head, m_popBuffer.size()); } m_head = (m_head + m_popBuffer.size()) % m_capacity; m_popFrame.byteSize = m_popBuffer.size(); m_popFrame.frameSize = m_popFrame.byteSize / m_channels / m_pointByteSize; m_full = false; return &m_popFrame; } uint32_t PcmStreamBuffer::availableByteSize() const { if (m_full) { return m_capacity; } else if (m_tail >= m_head) { return m_tail - m_head; } else { return m_capacity + m_tail - m_head; } }