Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
#include <memory>
#include <utility>

namespace audioapi {
AndroidFileWriterBackend::AndroidFileWriterBackend(
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
const std::shared_ptr<AudioFileProperties> &fileProperties)
: AudioFileWriter(audioEventHandlerRegistry, fileProperties) {

auto offloaderLambda = [this](WriterData data) {
taskOffloaderFunction(data);
};
offloader_ = std::make_unique<task_offloader::TaskOffloader<
WriterData,
FILE_WRITER_SPSC_OVERFLOW_STRATEGY,
FILE_WRITER_SPSC_WAIT_STRATEGY>>(FILE_WRITER_CHANNEL_CAPACITY, offloaderLambda);
}

void AndroidFileWriterBackend::writeAudioData(void *data, int numFrames) {
offloader_->getSender()->send({data, numFrames});
}
} // namespace audioapi
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#pragma once

#include <audioapi/core/utils/AudioFileWriter.h>
#include <audioapi/utils/SpscChannel.hpp>
#include <audioapi/utils/TaskOffloader.hpp>
#include <tuple>
#include <string>
#include <memory>
#include <audioapi/utils/Result.hpp>

struct WriterData {
void *data;
int numFrames;
};

namespace audioapi {

class AudioFileProperties;
Expand All @@ -14,11 +21,10 @@ class AndroidFileWriterBackend : public AudioFileWriter {
public:
explicit AndroidFileWriterBackend(
const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
const std::shared_ptr<AudioFileProperties> &fileProperties)
: AudioFileWriter(audioEventHandlerRegistry, fileProperties) {}
const std::shared_ptr<AudioFileProperties> &fileProperties);

virtual OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) = 0;
virtual bool writeAudioData(void *data, int numFrames) = 0;
void writeAudioData(void *data, int numFrames);

std::string getFilePath() const override { return filePath_; }
double getCurrentDuration() const override { return static_cast<double>(framesWritten_.load(std::memory_order_acquire)) / streamSampleRate_; }
Expand All @@ -27,7 +33,11 @@ class AndroidFileWriterBackend : public AudioFileWriter {
float streamSampleRate_{0};
int32_t streamChannelCount_{0};
int32_t streamMaxBufferSize_{0};
std::string filePath_{""};
std::string filePath_;

// delay initialization of offloader until prepare is called
std::unique_ptr<task_offloader::TaskOffloader<WriterData, FILE_WRITER_SPSC_OVERFLOW_STRATEGY, FILE_WRITER_SPSC_WAIT_STRATEGY>> offloader_;
virtual void taskOffloaderFunction(WriterData data) = 0;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,8 @@ Result<NoneType, std::string> AndroidRecorderCallback::prepare(
};
offloader_ = std::make_unique<task_offloader::TaskOffloader<
CallbackData,
AudioRecorderCallback::RECORDER_CALLBACK_SPSC_OVERFLOW_STRATEGY,
AudioRecorderCallback::RECORDER_CALLBACK_SPSC_WAIT_STRATEGY>>(
AudioRecorderCallback::RECORDER_CALLBACK_CHANNEL_CAPACITY, offloaderLambda);
RECORDER_CALLBACK_SPSC_OVERFLOW_STRATEGY,
RECORDER_CALLBACK_SPSC_WAIT_STRATEGY>>(RECORDER_CALLBACK_CHANNEL_CAPACITY, offloaderLambda);
return Result<NoneType, std::string>::Ok(None);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class AndroidRecorderCallback : public AudioRecorderCallback {

private:
// delay initialization of offloader until prepare is called
std::unique_ptr<task_offloader::TaskOffloader<CallbackData, AudioRecorderCallback::RECORDER_CALLBACK_SPSC_OVERFLOW_STRATEGY, AudioRecorderCallback::RECORDER_CALLBACK_SPSC_WAIT_STRATEGY>> offloader_;
std::unique_ptr<task_offloader::TaskOffloader<CallbackData, RECORDER_CALLBACK_SPSC_OVERFLOW_STRATEGY, RECORDER_CALLBACK_SPSC_WAIT_STRATEGY>> offloader_;
void taskOffloaderFunction(CallbackData data);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ OpenFileResult FFmpegAudioFileWriter::openFile(
return OpenFileResult::Err("Unsupported codec for the given file format");
}

auto offloaderLambda = [this](WriterData data) {
taskOffloaderFunction(data);
};

offloader_ = std::make_unique<task_offloader::TaskOffloader<
WriterData,
FILE_WRITER_SPSC_OVERFLOW_STRATEGY,
FILE_WRITER_SPSC_WAIT_STRATEGY>>(FILE_WRITER_CHANNEL_CAPACITY, offloaderLambda);

return initializeFormatContext(codec)
.and_then([this, codec](auto) { return configureAndOpenCodec(codec); })
.and_then([this](auto) { return initializeStream(); })
Expand Down Expand Up @@ -119,31 +128,27 @@ CloseFileResult FFmpegAudioFileWriter::closeFile() {
if (writeEncodedPackets() < 0) {
return CloseFileResult::Err("Failed to drain encoder packets");
}
offloader_.reset();

return finalizeOutput();
}

/// @brief Writes audio data to the currently opened file.
/// This method should be called only from the audio thread (or audio side-effect thread in the future).
/// @param data Pointer to the audio data buffer (interleaved float samples) as returned by Oboe stream.
/// @param numFrames Number of audio frames in the data buffer.
/// @returns True if the data was written successfully, false otherwise.
bool FFmpegAudioFileWriter::writeAudioData(void *data, int numFrames) {
void FFmpegAudioFileWriter::taskOffloaderFunction(WriterData data) {
auto [audioData, numFrames] = data;
if (!isFileOpen()) {
return false;
return;
}

if (!resampleAndPushToFifo(data, numFrames)) {
return false;
if (!resampleAndPushToFifo(audioData, numFrames)) {
return;
}

framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);

if (processFifo(false) < 0) {
return false;
return;
}

return true;
}

/// @brief Initializes the FFmpeg format context for the output file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class FFmpegAudioFileWriter : public AndroidFileWriterBackend {
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) override;
CloseFileResult closeFile() override;

bool writeAudioData(void *data, int numFrames) override;

private:
av_unique_ptr<AVCodecContext> encoderCtx_{nullptr};
Expand Down Expand Up @@ -64,6 +63,7 @@ class FFmpegAudioFileWriter : public AndroidFileWriterBackend {

// Finalization helper methods
CloseFileResult finalizeOutput();
void taskOffloaderFunction(WriterData data) override;
};

} // namespace android::ffmpeg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ MiniAudioFileWriter::~MiniAudioFileWriter() {
}

if (converter_ != nullptr) {
ma_data_converter_uninit(converter_.get(), NULL);
ma_data_converter_uninit(converter_.get(), nullptr);
converter_.reset();
}

if (processingBuffer_ != nullptr) {
ma_free(processingBuffer_, NULL);
ma_free(processingBuffer_, nullptr);
processingBuffer_ = nullptr;
processingBufferLength_ = 0;
}
Expand Down Expand Up @@ -103,6 +103,15 @@ OpenFileResult MiniAudioFileWriter::openFile(
"Failed to initialize encoder" + std::string(ma_result_description(result)));
}

auto offloaderLambda = [this](WriterData data) {
taskOffloaderFunction(data);
};

offloader_ = std::make_unique<task_offloader::TaskOffloader<
WriterData,
FILE_WRITER_SPSC_OVERFLOW_STRATEGY,
FILE_WRITER_SPSC_WAIT_STRATEGY>>(FILE_WRITER_CHANNEL_CAPACITY, offloaderLambda);

isFileOpen_.store(true, std::memory_order_release);
return OpenFileResult ::Ok(filePath_);
}
Expand All @@ -125,12 +134,12 @@ CloseFileResult MiniAudioFileWriter::closeFile() {
}

if (converter_ != nullptr) {
ma_data_converter_uninit(converter_.get(), NULL);
ma_data_converter_uninit(converter_.get(), nullptr);
converter_.reset();
}

if (processingBuffer_ != nullptr) {
ma_free(processingBuffer_, NULL);
ma_free(processingBuffer_, nullptr);
processingBuffer_ = nullptr;
processingBufferLength_ = 0;
}
Expand All @@ -141,7 +150,7 @@ CloseFileResult MiniAudioFileWriter::closeFile() {

ma_decoder decoder;

if (ma_decoder_init_file(filePath_.c_str(), NULL, &decoder) == MA_SUCCESS) {
if (ma_decoder_init_file(filePath_.c_str(), nullptr, &decoder) == MA_SUCCESS) {
ma_uint64 frameCount = 0;

if (ma_decoder_get_length_in_pcm_frames(&decoder, &frameCount) == MA_SUCCESS) {
Expand All @@ -159,6 +168,7 @@ CloseFileResult MiniAudioFileWriter::closeFile() {
fclose(file);
fileSizeInMB = static_cast<double>(fileSizeInBytes) / MB_IN_BYTES;
}
offloader_.reset();

filePath_ = "";
return CloseFileResult ::Ok({fileSizeInMB, durationInSeconds});
Expand All @@ -167,33 +177,30 @@ CloseFileResult MiniAudioFileWriter::closeFile() {
/// @brief Writes audio data to the file.
/// If possible (sample format, channel count, and interleaving matches),
/// the data is written directly, otherwise in-memory conversion is performed first
/// It should be called only on the audio thread.
/// @param data Pointer to the audio data buffer. (Interleaved float32 format - as oboe likes it)
/// @param numFrames Number of audio frames to write.
/// @return True if the write operation was successful, false otherwise.
bool MiniAudioFileWriter::writeAudioData(void *data, int numFrames) {
void MiniAudioFileWriter::taskOffloaderFunction(WriterData data) {
auto [audioData, numFrames] = data;
ma_uint64 framesWritten = 0;
ma_result result;

if (!isFileOpen()) {
return false;
return;
}

if (!isConverterRequired()) {
result = ma_encoder_write_pcm_frames(encoder_.get(), data, numFrames, &framesWritten);
result = ma_encoder_write_pcm_frames(encoder_.get(), audioData, numFrames, &framesWritten);

if (result != MA_SUCCESS) {
invokeOnErrorCallback(
"Failed to write audio data to file: " + filePath_ +
std::string(ma_result_description(result)));
return false;
return;
}

framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
return result == MA_SUCCESS;
return;
}

ma_uint64 convertedFrameCount = convertBuffer(data, numFrames);
ma_uint64 convertedFrameCount = convertBuffer(audioData, numFrames);

result = ma_encoder_write_pcm_frames(
encoder_.get(), processingBuffer_, convertedFrameCount, &framesWritten);
Expand All @@ -202,11 +209,10 @@ bool MiniAudioFileWriter::writeAudioData(void *data, int numFrames) {
invokeOnErrorCallback(
"Failed to write converted audio data to file: " + filePath_ +
std::string(ma_result_description(result)));
return false;
return;
}

framesWritten_.fetch_add(numFrames, std::memory_order_acq_rel);
return result == MA_SUCCESS;
}

/// @brief Converts the audio data buffer if necessary.
Expand Down Expand Up @@ -247,7 +253,7 @@ ma_result MiniAudioFileWriter::initializeConverterIfNeeded() {
fileProperties_->sampleRate);

converter_ = std::make_unique<ma_data_converter>();
result = ma_data_converter_init(&converterConfig, NULL, converter_.get());
result = ma_data_converter_init(&converterConfig, nullptr, converter_.get());

if (result != MA_SUCCESS) {
return result;
Expand All @@ -258,7 +264,7 @@ ma_result MiniAudioFileWriter::initializeConverterIfNeeded() {

processingBuffer_ = ma_malloc(
processingBufferLength_ * fileProperties_->channelCount * ma_get_bytes_per_sample(dataFormat),
NULL);
nullptr);

return MA_SUCCESS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class MiniAudioFileWriter : public AndroidFileWriterBackend {
OpenFileResult openFile(float streamSampleRate, int32_t streamChannelCount, int32_t streamMaxBufferSize, const std::string &fileNameOverride) override;
CloseFileResult closeFile() override;

bool writeAudioData(void *data, int numFrames) override;

private:
std::atomic<bool> isConverterRequired_{false};
Expand All @@ -35,6 +34,7 @@ class MiniAudioFileWriter : public AndroidFileWriterBackend {
ma_uint64 convertBuffer(void *data, int numFrames);

bool isConverterRequired();
void taskOffloaderFunction(WriterData data) override;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <audioapi/utils/Result.hpp>
#include <audioapi/utils/SpscChannel.hpp>
#include <atomic>
#include <memory>
#include <string>
Expand Down Expand Up @@ -39,6 +40,11 @@ class AudioFileWriter {

std::shared_ptr<AudioFileProperties> fileProperties_;
std::shared_ptr<AudioEventHandlerRegistry> audioEventHandlerRegistry_;

static constexpr auto FILE_WRITER_SPSC_OVERFLOW_STRATEGY =
channels::spsc::OverflowStrategy::OVERWRITE_ON_FULL;
static constexpr auto FILE_WRITER_SPSC_WAIT_STRATEGY = channels::spsc::WaitStrategy::ATOMIC_WAIT;
static constexpr auto FILE_WRITER_CHANNEL_CAPACITY = 64;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <audioapi/core/utils/AudioFileWriter.h>
#include <audioapi/utils/Result.hpp>
#include <audioapi/utils/SpscChannel.hpp>
#include <audioapi/utils/TaskOffloader.hpp>
#include <memory>
#include <string>
#include <tuple>
Expand All @@ -15,6 +17,11 @@ typedef struct objc_object AudioBufferList;
typedef struct objc_object AVAudioConverter;
#endif // __OBJC__

struct WriterData {
const AudioBufferList *audioBufferList;
int numFrames;
};

namespace audioapi {

class AudioFileProperties;
Expand All @@ -33,7 +40,7 @@ class IOSFileWriter : public AudioFileWriter {
const std::string &fileNameOverride);
Result<std::tuple<double, double>, std::string> closeFile() override;

bool writeAudioData(const AudioBufferList *audioBufferList, int numFrames);
void writeAudioData(const AudioBufferList *audioBufferList, int numFrames);
double getCurrentDuration() const override;

std::string getFilePath() const override;
Expand All @@ -49,6 +56,15 @@ class IOSFileWriter : public AudioFileWriter {

AVAudioPCMBuffer *converterInputBuffer_;
AVAudioPCMBuffer *converterOutputBuffer_;

private:
// delay initialization of offloader until prepare is called
std::unique_ptr<task_offloader::TaskOffloader<
WriterData,
FILE_WRITER_SPSC_OVERFLOW_STRATEGY,
FILE_WRITER_SPSC_WAIT_STRATEGY>>
offloader_;
void taskOffloaderFunction(WriterData data);
};

} // namespace audioapi
Loading