Files
emulate/app/src/main/cpp/audio_engine.cpp

115 lines
3.7 KiB
C++
Raw Normal View History

#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);
}