From ab0f065de63cae6021ee5988a4d6eca7a0778ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 27 Sep 2019 21:53:15 +0200 Subject: [PATCH] Add very bad Audio Output Buffer on Android --- android/app/src/main/cpp/audio-output.cpp | 133 +++++++++++++++++++--- lib/src/ctrl.c | 2 +- 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/android/app/src/main/cpp/audio-output.cpp b/android/app/src/main/cpp/audio-output.cpp index 27ebaa8..f9f3ad1 100644 --- a/android/app/src/main/cpp/audio-output.cpp +++ b/android/app/src/main/cpp/audio-output.cpp @@ -22,20 +22,58 @@ #include -#define WRITE_TIMEOUT_MS 8 +#define BUFFER_SIZE_DEFAULT 409600 + +class AudioOutput; + +class AudioOutputCallback: public oboe::AudioStreamCallback +{ +private: + AudioOutput *audio_output; + +public: + AudioOutputCallback(AudioOutput *audio_output) : audio_output(audio_output) {} + oboe::DataCallbackResult onAudioReady(oboe::AudioStream *stream, void *audioData, int32_t numFrames) override; + void onErrorBeforeClose(oboe::AudioStream *stream, oboe::Result error) override; + void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) override; +}; struct AudioOutput { ChiakiLog *log; oboe::ManagedStream stream; - ChiakiMutex stream_mutex; + AudioOutputCallback stream_callback; + + ChiakiMutex buffer_mutex; // TODO: make lockless + uint8_t *buffer; + size_t buffer_size; // total size + size_t buffer_start; + size_t buffer_length; // filled size + + AudioOutput() : stream_callback(this) {} }; extern "C" void *android_chiaki_audio_output_new(ChiakiLog *log) { auto r = new AudioOutput(); r->log = log; - chiaki_mutex_init(&r->stream_mutex, false); + + if(chiaki_mutex_init(&r->buffer_mutex, false) != CHIAKI_ERR_SUCCESS) + { + delete r; + return nullptr; + } + r->buffer_size = BUFFER_SIZE_DEFAULT; + r->buffer = (uint8_t *)malloc(r->buffer_size); + if(!r->buffer) + { + chiaki_mutex_fini(&r->buffer_mutex); + delete r; + return nullptr; + } + r->buffer_start = 0; + r->buffer_length = 0; + return r; } @@ -44,21 +82,23 @@ extern "C" void android_chiaki_audio_output_free(void *audio_output) if(!audio_output) return; auto ao = reinterpret_cast(audio_output); - chiaki_mutex_fini(&ao->stream_mutex); + ao->stream = nullptr; + free(ao->buffer); + chiaki_mutex_fini(&ao->buffer_mutex); delete ao; } extern "C" void android_chiaki_audio_output_settings(uint32_t channels, uint32_t rate, void *audio_output) { auto ao = reinterpret_cast(audio_output); - ChiakiMutexLock lock(&ao->stream_mutex); oboe::AudioStreamBuilder builder; builder.setPerformanceMode(oboe::PerformanceMode::LowLatency) ->setSharingMode(oboe::SharingMode::Exclusive) ->setFormat(oboe::AudioFormat::I16) ->setChannelCount(channels) - ->setSampleRate(rate); + ->setSampleRate(rate) + ->setCallback(&ao->stream_callback); auto result = builder.openManagedStream(ao->stream); if(result == oboe::Result::OK) @@ -76,19 +116,78 @@ extern "C" void android_chiaki_audio_output_settings(uint32_t channels, uint32_t extern "C" void android_chiaki_audio_output_frame(int16_t *buf, size_t samples_count, void *audio_output) { auto ao = reinterpret_cast(audio_output); - ChiakiMutexLock lock(&ao->stream_mutex); + ChiakiMutexLock lock(&ao->buffer_mutex); - if(ao->stream->getFormat() != oboe::AudioFormat::I16) + size_t buf_size = samples_count * sizeof(int16_t); + if(ao->buffer_size - ao->buffer_length < buf_size) { - CHIAKI_LOGE(ao->log, "Oboe stream has invalid format for pushing samples"); - return; + CHIAKI_LOGW(ao->log, "Audio Output Buffer Overflow!"); + buf_size = ao->buffer_size - ao->buffer_length; } - int32_t samples_per_frame = ao->stream->getBytesPerFrame() / ao->stream->getBytesPerSample(); - if(samples_count % samples_per_frame != 0) - CHIAKI_LOGW(ao->log, "Received %llu samples, which is not dividable by %llu samples per frame", (unsigned long long)samples_count, (unsigned long long)samples_per_frame); + if(!buf_size) + return; - size_t frames = samples_count / samples_per_frame; - if(frames > 0) - ao->stream->write(buf, frames, WRITE_TIMEOUT_MS * 1000 * 1000); -} \ No newline at end of file + size_t end = (ao->buffer_start + ao->buffer_length) % ao->buffer_size; + ao->buffer_length += buf_size; + if(end + buf_size > ao->buffer_size) + { + size_t part = ao->buffer_size - end; + memcpy(ao->buffer + end, buf, part); + buf_size -= part; + buf = (int16_t *)(((uint8_t *)buf) + part); + end = 0; + } + memcpy(ao->buffer + end, buf, buf_size); +} + +oboe::DataCallbackResult AudioOutputCallback::onAudioReady(oboe::AudioStream *stream, void *audio_data, int32_t num_frames) +{ + if(stream->getFormat() != oboe::AudioFormat::I16) + { + CHIAKI_LOGE(audio_output->log, "Oboe stream has invalid format in callback"); + return oboe::DataCallbackResult::Stop; + } + + int32_t bytes_per_frame = stream->getBytesPerFrame(); + size_t buf_size_requested = static_cast(bytes_per_frame * num_frames); + + size_t buf_size_delivered = buf_size_requested; + uint8_t *buf = (uint8_t *)audio_data; + + { + ChiakiMutexLock lock(&audio_output->buffer_mutex); + if(audio_output->buffer_length < buf_size_delivered) + { + CHIAKI_LOGW(audio_output->log, "Audio Output Buffer Underflow!"); + buf_size_delivered = audio_output->buffer_length; + } + + if(audio_output->buffer_start + buf_size_delivered > audio_output->buffer_size) + { + size_t part = audio_output->buffer_size - audio_output->buffer_start; + memcpy(buf, audio_output->buffer + audio_output->buffer_start, part); + memcpy(buf + part, audio_output->buffer, buf_size_delivered - part); + } + else + memcpy(buf, audio_output->buffer + audio_output->buffer_start, buf_size_delivered); + + audio_output->buffer_start = (audio_output->buffer_start + buf_size_delivered) % audio_output->buffer_size; + audio_output->buffer_length -= buf_size_delivered; + } + + if(buf_size_delivered < buf_size_requested) + memset(buf + buf_size_delivered, 0, buf_size_requested - buf_size_delivered); + + return oboe::DataCallbackResult::Continue; +} + +void AudioOutputCallback::onErrorBeforeClose(oboe::AudioStream *stream, oboe::Result error) +{ + CHIAKI_LOGE(audio_output->log, "Oboe reported error before close: %s", oboe::convertToText(error)); +} + +void AudioOutputCallback::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result error) +{ + CHIAKI_LOGE(audio_output->log, "Oboe reported error after close: %s", oboe::convertToText(error)); +} diff --git a/lib/src/ctrl.c b/lib/src/ctrl.c index 21c64c7..c9f7b0e 100644 --- a/lib/src/ctrl.c +++ b/lib/src/ctrl.c @@ -578,7 +578,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) if(err != CHIAKI_ERR_SUCCESS) { if(err != CHIAKI_ERR_CANCELED) - CHIAKI_LOGE(session->log, "Failed to receive ctrl request response"); + CHIAKI_LOGE(session->log, "Failed to receive ctrl request response: %s", chiaki_error_string(err)); goto error; }