#include "audio_engine.h" #include #include #include #define LOG_TAG "AudioEngine" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) // --- RingBuffer --- void RingBuffer::push(const int16_t* data, size_t frames) { size_t wp = write_pos_.load(std::memory_order_relaxed); size_t rp = read_pos_.load(std::memory_order_acquire); for (size_t i = 0; i < frames; i++) { size_t next = (wp + 1) % CAPACITY; if (next == rp) break; // full, drop samples buffer_[wp * 2] = data[i * 2]; buffer_[wp * 2 + 1] = data[i * 2 + 1]; wp = next; } write_pos_.store(wp, std::memory_order_release); } size_t RingBuffer::pop(int16_t* out, size_t frames) { size_t rp = read_pos_.load(std::memory_order_relaxed); size_t wp = write_pos_.load(std::memory_order_acquire); size_t read = 0; while (read < frames && rp != wp) { out[read * 2] = buffer_[rp * 2]; out[read * 2 + 1] = buffer_[rp * 2 + 1]; rp = (rp + 1) % CAPACITY; read++; } read_pos_.store(rp, std::memory_order_release); return read; } size_t RingBuffer::available() const { size_t wp = write_pos_.load(std::memory_order_acquire); size_t rp = read_pos_.load(std::memory_order_acquire); return (wp >= rp) ? (wp - rp) : (CAPACITY - rp + wp); } // --- AudioEngine --- aaudio_data_callback_result_t AudioEngine::dataCallback( AAudioStream* /*stream*/, void* userData, void* audioData, int32_t numFrames) { auto* engine = static_cast(userData); auto* output = static_cast(audioData); if (!engine->isEnabled()) { memset(output, 0, numFrames * 2 * sizeof(int16_t)); return AAUDIO_CALLBACK_RESULT_CONTINUE; } size_t read = engine->ringBuffer().pop(output, numFrames); // Fill remainder with silence to prevent noise if (read < static_cast(numFrames)) { memset(output + read * 2, 0, (numFrames - read) * 2 * sizeof(int16_t)); } return AAUDIO_CALLBACK_RESULT_CONTINUE; } bool AudioEngine::start(int32_t sampleRate) { AAudioStreamBuilder* builder = nullptr; aaudio_result_t result = AAudio_createStreamBuilder(&builder); if (result != AAUDIO_OK) { LOGE("Failed to create stream builder: %d", result); return false; } AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT); AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED); AAudioStreamBuilder_setSampleRate(builder, sampleRate); AAudioStreamBuilder_setChannelCount(builder, 2); AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16); AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); AAudioStreamBuilder_setDataCallback(builder, dataCallback, this); result = AAudioStreamBuilder_openStream(builder, &stream_); AAudioStreamBuilder_delete(builder); if (result != AAUDIO_OK) { LOGE("Failed to open stream: %d", result); return false; } result = AAudioStream_requestStart(stream_); if (result != AAUDIO_OK) { LOGE("Failed to start stream: %d", result); AAudioStream_close(stream_); stream_ = nullptr; return false; } LOGD("Audio started: %d Hz stereo", sampleRate); return true; } void AudioEngine::stop() { if (stream_) { AAudioStream_requestStop(stream_); AAudioStream_close(stream_); stream_ = nullptr; LOGD("Audio stopped"); } } void AudioEngine::pushSamples(const int16_t* data, size_t frames) { ring_buffer_.push(data, frames); }