mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-19 21:13:12 -07:00
Add very bad Audio Output Buffer on Android
This commit is contained in:
parent
3330f010b2
commit
ab0f065de6
2 changed files with 117 additions and 18 deletions
|
@ -22,20 +22,58 @@
|
||||||
|
|
||||||
#include <oboe/Oboe.h>
|
#include <oboe/Oboe.h>
|
||||||
|
|
||||||
#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
|
struct AudioOutput
|
||||||
{
|
{
|
||||||
ChiakiLog *log;
|
ChiakiLog *log;
|
||||||
oboe::ManagedStream stream;
|
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)
|
extern "C" void *android_chiaki_audio_output_new(ChiakiLog *log)
|
||||||
{
|
{
|
||||||
auto r = new AudioOutput();
|
auto r = new AudioOutput();
|
||||||
r->log = log;
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,21 +82,23 @@ extern "C" void android_chiaki_audio_output_free(void *audio_output)
|
||||||
if(!audio_output)
|
if(!audio_output)
|
||||||
return;
|
return;
|
||||||
auto ao = reinterpret_cast<AudioOutput *>(audio_output);
|
auto ao = reinterpret_cast<AudioOutput *>(audio_output);
|
||||||
chiaki_mutex_fini(&ao->stream_mutex);
|
ao->stream = nullptr;
|
||||||
|
free(ao->buffer);
|
||||||
|
chiaki_mutex_fini(&ao->buffer_mutex);
|
||||||
delete ao;
|
delete ao;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void android_chiaki_audio_output_settings(uint32_t channels, uint32_t rate, void *audio_output)
|
extern "C" void android_chiaki_audio_output_settings(uint32_t channels, uint32_t rate, void *audio_output)
|
||||||
{
|
{
|
||||||
auto ao = reinterpret_cast<AudioOutput *>(audio_output);
|
auto ao = reinterpret_cast<AudioOutput *>(audio_output);
|
||||||
ChiakiMutexLock lock(&ao->stream_mutex);
|
|
||||||
|
|
||||||
oboe::AudioStreamBuilder builder;
|
oboe::AudioStreamBuilder builder;
|
||||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||||
->setSharingMode(oboe::SharingMode::Exclusive)
|
->setSharingMode(oboe::SharingMode::Exclusive)
|
||||||
->setFormat(oboe::AudioFormat::I16)
|
->setFormat(oboe::AudioFormat::I16)
|
||||||
->setChannelCount(channels)
|
->setChannelCount(channels)
|
||||||
->setSampleRate(rate);
|
->setSampleRate(rate)
|
||||||
|
->setCallback(&ao->stream_callback);
|
||||||
|
|
||||||
auto result = builder.openManagedStream(ao->stream);
|
auto result = builder.openManagedStream(ao->stream);
|
||||||
if(result == oboe::Result::OK)
|
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)
|
extern "C" void android_chiaki_audio_output_frame(int16_t *buf, size_t samples_count, void *audio_output)
|
||||||
{
|
{
|
||||||
auto ao = reinterpret_cast<AudioOutput *>(audio_output);
|
auto ao = reinterpret_cast<AudioOutput *>(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");
|
CHIAKI_LOGW(ao->log, "Audio Output Buffer Overflow!");
|
||||||
return;
|
buf_size = ao->buffer_size - ao->buffer_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t samples_per_frame = ao->stream->getBytesPerFrame() / ao->stream->getBytesPerSample();
|
if(!buf_size)
|
||||||
if(samples_count % samples_per_frame != 0)
|
return;
|
||||||
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);
|
|
||||||
|
|
||||||
size_t frames = samples_count / samples_per_frame;
|
size_t end = (ao->buffer_start + ao->buffer_length) % ao->buffer_size;
|
||||||
if(frames > 0)
|
ao->buffer_length += buf_size;
|
||||||
ao->stream->write(buf, frames, WRITE_TIMEOUT_MS * 1000 * 1000);
|
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<size_t>(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));
|
||||||
|
}
|
||||||
|
|
|
@ -578,7 +578,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl)
|
||||||
if(err != CHIAKI_ERR_SUCCESS)
|
if(err != CHIAKI_ERR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(err != CHIAKI_ERR_CANCELED)
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue