115 lines
3.7 KiB
C++
115 lines
3.7 KiB
C++
|
|
#include "audio_engine.h"
|
||
|
|
#include <android/log.h>
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
#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<AudioEngine*>(userData);
|
||
|
|
auto* output = static_cast<int16_t*>(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<size_t>(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);
|
||
|
|
}
|