libaaudio: implement callback
Use AudioTrack and AudioRecord TRANSFER_CALLBACK.
Add FixedBlockAdapter to provide fixed size callbacks.
Bug: 36489240
Test: CTS test_aaudio.cpp
Change-Id: Id2034dd640f878dd27fee6b43ad80a01c627dfd6
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index a016b49..1ee73bf 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -30,10 +30,14 @@
core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
utility/HandleTracker.cpp \
utility/AAudioUtilities.cpp \
+ utility/FixedBlockAdapter.cpp \
+ utility/FixedBlockReader.cpp \
+ utility/FixedBlockWriter.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
@@ -79,10 +83,14 @@
LOCAL_SRC_FILES = core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
utility/HandleTracker.cpp \
utility/AAudioUtilities.cpp \
+ utility/FixedBlockAdapter.cpp \
+ utility/FixedBlockReader.cpp \
+ utility/FixedBlockWriter.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 47c4774..90c619c 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -19,7 +19,7 @@
#include <utils/Log.h>
#include <cassert>
-#include <aaudio/AAudioDefinitions.h>
+#include <aaudio/AAudio.h>
#include "AudioEndpointParcelable.h"
#include "AudioEndpoint.h"
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 54f4870..1f9ce4f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -18,23 +18,19 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
-#include <stdint.h>
#include <assert.h>
#include <binder/IServiceManager.h>
#include <utils/Mutex.h>
#include <aaudio/AAudio.h>
+#include <utils/String16.h>
-#include "AudioClock.h"
-#include "AudioEndpointParcelable.h"
-#include "binding/AAudioStreamRequest.h"
-#include "binding/AAudioStreamConfiguration.h"
-#include "binding/IAAudioService.h"
+#include "utility/AudioClock.h"
+#include "AudioStreamInternal.h"
#include "binding/AAudioServiceMessage.h"
#include "core/AudioStreamBuilder.h"
-#include "AudioStreamInternal.h"
#define LOG_TIMESTAMPS 0
@@ -51,6 +47,11 @@
#define AAUDIO_SERVICE_NAME "AAudioService"
+#define MIN_TIMEOUT_NANOS (1000 * AAUDIO_NANOS_PER_MILLISECOND)
+
+// Wait at least this many times longer than the operation should take.
+#define MIN_TIMEOUT_OPERATIONS 4
+
// Helper function to get access to the "AAudioService" service.
// This code was modeled after frameworks/av/media/libaudioclient/AudioSystem.cpp
static const sp<IAAudioService> getAAudioService() {
@@ -151,6 +152,29 @@
mClockModel.setSampleRate(getSampleRate());
mClockModel.setFramesPerBurst(mFramesPerBurst);
+ if (getDataCallbackProc()) {
+ mCallbackFrames = builder.getFramesPerDataCallback();
+ if (mCallbackFrames > getBufferCapacity() / 2) {
+ ALOGE("AudioStreamInternal.open(): framesPerCallback too large");
+ service->closeStream(mServiceStreamHandle);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+
+ } else if (mCallbackFrames < 0) {
+ ALOGE("AudioStreamInternal.open(): framesPerCallback negative");
+ service->closeStream(mServiceStreamHandle);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+
+ }
+ if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
+ mCallbackFrames = mFramesPerBurst;
+ }
+
+ int32_t bytesPerFrame = getSamplesPerFrame()
+ * AAudioConvert_formatToSizeInBytes(getFormat());
+ int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
+ mCallbackBuffer = new uint8_t[callbackBufferSize];
+ }
+
setState(AAUDIO_STREAM_STATE_OPEN);
}
return result;
@@ -164,12 +188,69 @@
const sp<IAAudioService>& aaudioService = getAAudioService();
if (aaudioService == 0) return AAUDIO_ERROR_NO_SERVICE;
aaudioService->closeStream(serviceStreamHandle);
+ delete[] mCallbackBuffer;
return AAUDIO_OK;
} else {
return AAUDIO_ERROR_INVALID_HANDLE;
}
}
+// Render audio in the application callback and then write the data to the stream.
+void *AudioStreamInternal::callbackLoop() {
+ aaudio_result_t result = AAUDIO_OK;
+ aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
+ int32_t framesWritten = 0;
+ AAudioStream_dataCallback appCallback = getDataCallbackProc();
+ if (appCallback == nullptr) return NULL;
+
+ while (mCallbackEnabled.load() && isPlaying() && (result >= 0)) { // result might be a frame count
+ // Call application using the AAudio callback interface.
+ callbackResult = (*appCallback)(
+ (AAudioStream *) this,
+ getDataCallbackUserData(),
+ mCallbackBuffer,
+ mCallbackFrames);
+
+ if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
+ // Write audio data to stream
+ int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
+ result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos);
+ if (result == AAUDIO_ERROR_DISCONNECTED) {
+ if (getErrorCallbackProc() != nullptr) {
+ ALOGD("AudioStreamAAudio(): callbackLoop() stream disconnected");
+ (*getErrorCallbackProc())(
+ (AAudioStream *) this,
+ getErrorCallbackUserData(),
+ AAUDIO_OK);
+ }
+ break;
+ } else if (result != mCallbackFrames) {
+ ALOGE("AudioStreamAAudio(): callbackLoop() wrote %d / %d",
+ framesWritten, mCallbackFrames);
+ break;
+ }
+ } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
+ ALOGD("AudioStreamAAudio(): callback returned AAUDIO_CALLBACK_RESULT_STOP");
+ break;
+ }
+ }
+
+ ALOGD("AudioStreamAAudio(): callbackLoop() exiting, result = %d, isPlaying() = %d",
+ result, (int) isPlaying());
+ return NULL; // TODO review
+}
+
+static void *aaudio_callback_thread_proc(void *context)
+{
+ AudioStreamInternal *stream = (AudioStreamInternal *)context;
+ //LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
+ if (stream != NULL) {
+ return stream->callbackLoop();
+ } else {
+ return NULL;
+ }
+}
+
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
@@ -178,35 +259,81 @@
return AAUDIO_ERROR_INVALID_STATE;
}
const sp<IAAudioService>& aaudioService = getAAudioService();
- if (aaudioService == 0) return AAUDIO_ERROR_NO_SERVICE;
+ if (aaudioService == 0) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
processTimestamp(0, startTime);
setState(AAUDIO_STREAM_STATE_STARTING);
- return aaudioService->startStream(mServiceStreamHandle);
+ aaudio_result_t result = aaudioService->startStream(mServiceStreamHandle);
+
+ if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
+ // Launch the callback loop thread.
+ int64_t periodNanos = mCallbackFrames
+ * AAUDIO_NANOS_PER_SECOND
+ / getSampleRate();
+ mCallbackEnabled.store(true);
+ result = createThread(periodNanos, aaudio_callback_thread_proc, this);
+ }
+ return result;
}
-aaudio_result_t AudioStreamInternal::requestPause()
+int64_t AudioStreamInternal::calculateReasonableTimeout(int32_t framesPerOperation) {
+
+ // Wait for at least a second or some number of callbacks to join the thread.
+ int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
+ / getSampleRate();
+ if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
+ timeoutNanoseconds = MIN_TIMEOUT_NANOS;
+ }
+ return timeoutNanoseconds;
+}
+
+aaudio_result_t AudioStreamInternal::stopCallback()
+{
+ if (isDataCallbackActive()) {
+ mCallbackEnabled.store(false);
+ return joinThread(NULL, calculateReasonableTimeout(mCallbackFrames));
+ } else {
+ return AAUDIO_OK;
+ }
+}
+
+aaudio_result_t AudioStreamInternal::requestPauseInternal()
{
ALOGD("AudioStreamInternal(): pause()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
const sp<IAAudioService>& aaudioService = getAAudioService();
- if (aaudioService == 0) return AAUDIO_ERROR_NO_SERVICE;
+ if (aaudioService == 0) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_PAUSING);
return aaudioService->pauseStream(mServiceStreamHandle);
}
+aaudio_result_t AudioStreamInternal::requestPause()
+{
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+ return requestPauseInternal();
+}
+
aaudio_result_t AudioStreamInternal::requestFlush() {
ALOGD("AudioStreamInternal(): flush()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
const sp<IAAudioService>& aaudioService = getAAudioService();
- if (aaudioService == 0) return AAUDIO_ERROR_NO_SERVICE;
-setState(AAUDIO_STREAM_STATE_FLUSHING);
+ if (aaudioService == 0) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
+ setState(AAUDIO_STREAM_STATE_FLUSHING);
return aaudioService->flushStream(mServiceStreamHandle);
}
@@ -260,18 +387,20 @@
return aaudioService->unregisterAudioThread(mServiceStreamHandle, gettid());
}
-// TODO use aaudio_clockid_t all the way down to AudioClock
aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) {
-// TODO implement using real HAL
+ // TODO implement using real HAL
int64_t time = AudioClock::getNanoseconds();
*framePosition = mClockModel.convertTimeToPosition(time);
*timeNanoseconds = time + (10 * AAUDIO_NANOS_PER_MILLISECOND); // Fake hardware delay
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamInternal::updateState() {
+aaudio_result_t AudioStreamInternal::updateStateWhileWaiting() {
+ if (isDataCallbackActive()) {
+ return AAUDIO_OK; // state is getting updated by the callback thread read/write call
+ }
return processCommands();
}
@@ -485,43 +614,6 @@
return framesWritten;
}
-aaudio_result_t AudioStreamInternal::waitForStateChange(aaudio_stream_state_t currentState,
- aaudio_stream_state_t *nextState,
- int64_t timeoutNanoseconds)
-
-{
- aaudio_result_t result = processCommands();
-// ALOGD("AudioStreamInternal::waitForStateChange() - processCommands() returned %d", result);
- if (result != AAUDIO_OK) {
- return result;
- }
- // TODO replace this polling with a timed sleep on a futex on the message queue
- int32_t durationNanos = 5 * AAUDIO_NANOS_PER_MILLISECOND;
- aaudio_stream_state_t state = getState();
-// ALOGD("AudioStreamInternal::waitForStateChange() - state = %d", state);
- while (state == currentState && timeoutNanoseconds > 0) {
- // TODO use futex from service message queue
- if (durationNanos > timeoutNanoseconds) {
- durationNanos = timeoutNanoseconds;
- }
- AudioClock::sleepForNanos(durationNanos);
- timeoutNanoseconds -= durationNanos;
-
- result = processCommands();
- if (result != AAUDIO_OK) {
- return result;
- }
-
- state = getState();
-// ALOGD("AudioStreamInternal::waitForStateChange() - state = %d", state);
- }
- if (nextState != nullptr) {
- *nextState = state;
- }
- return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK;
-}
-
-
void AudioStreamInternal::processTimestamp(uint64_t position, int64_t time) {
mClockModel.processTimestamp( position, time);
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 6f3a7ac..9a15a9b 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -53,7 +53,7 @@
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t updateState() override;
+ virtual aaudio_result_t updateStateWhileWaiting() override;
// =========== End ABSTRACT methods ===========================
virtual aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -64,10 +64,6 @@
int32_t numFrames,
int64_t timeoutNanoseconds) override;
- virtual aaudio_result_t waitForStateChange(aaudio_stream_state_t currentState,
- aaudio_stream_state_t *nextState,
- int64_t timeoutNanoseconds) override;
-
virtual aaudio_result_t setBufferSize(int32_t requestedFrames) override;
virtual int32_t getBufferSize() const override;
@@ -86,10 +82,17 @@
virtual aaudio_result_t unregisterThread() override;
+ // Called internally from 'C'
+ void *callbackLoop();
+
protected:
aaudio_result_t processCommands();
+ aaudio_result_t requestPauseInternal();
+
+ aaudio_result_t stopCallback();
+
/**
* Low level write that will not block. It will just write as much as it can.
*
@@ -108,17 +111,22 @@
aaudio_result_t onTimestampFromServer(AAudioServiceMessage *message);
+ // Calculate timeout for an operation involving framesPerOperation.
+ int64_t calculateReasonableTimeout(int32_t framesPerOperation);
+
private:
IsochronousClockModel mClockModel;
AudioEndpoint mAudioEndpoint;
aaudio_handle_t mServiceStreamHandle;
EndpointDescriptor mEndpointDescriptor;
+ uint8_t *mCallbackBuffer = nullptr;
+ int32_t mCallbackFrames = 0;
+
// Offset from underlying frame position.
int64_t mFramesOffsetFromService = 0;
int64_t mLastFramesRead = 0;
int32_t mFramesPerBurst;
int32_t mXRunCount = 0;
-
void processTimestamp(uint64_t position, int64_t time);
};
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index 4c8aabc..c278c8b 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -19,7 +19,6 @@
#include <utils/Log.h>
#include <stdint.h>
-#include <aaudio/AAudioDefinitions.h>
#include "utility/AudioClock.h"
#include "IsochronousClockModel.h"
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 524c286..205c341 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#ifndef AAUDIO_ISOCHRONOUSCLOCKMODEL_H
-#define AAUDIO_ISOCHRONOUSCLOCKMODEL_H
+#ifndef AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#define AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
#include <stdint.h>
-#include <aaudio/AAudio.h>
namespace aaudio {
@@ -107,4 +106,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_ISOCHRONOUSCLOCKMODEL_H
+#endif //AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 52bad70..bc2f281 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -114,53 +114,79 @@
AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
int32_t deviceId)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setDeviceId(deviceId);
}
AAUDIO_API void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder,
int32_t sampleRate)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setSampleRate(sampleRate);
}
AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
int32_t samplesPerFrame)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setSamplesPerFrame(samplesPerFrame);
}
AAUDIO_API void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder,
aaudio_direction_t direction)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setDirection(direction);
}
-
AAUDIO_API void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder,
aaudio_audio_format_t format)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setFormat(format);
}
AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder,
aaudio_sharing_mode_t sharingMode)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setSharingMode(sharingMode);
}
AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder,
int32_t frames)
{
- AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);;
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setBufferCapacity(frames);
}
+AAUDIO_API void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder,
+ AAudioStream_dataCallback callback,
+ void *userData)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
+ streamBuilder->setDataCallbackProc(callback);
+ streamBuilder->setDataCallbackUserData(userData);
+}
+AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder,
+ AAudioStream_errorCallback callback,
+ void *userData)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
+ streamBuilder->setErrorCallbackProc(callback);
+ streamBuilder->setErrorCallbackUserData(userData);
+}
+
+AAUDIO_API void AAudioStreamBuilder_setFramesPerDataCallback(AAudioStreamBuilder* builder,
+ int32_t frames)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ ALOGD("%s: frames = %d", __func__, frames);
+ streamBuilder->setFramesPerDataCallback(frames);
+}
+
static aaudio_result_t AAudioInternal_openStream(AudioStreamBuilder *streamBuilder,
AAudioStream** streamPtr)
{
@@ -276,6 +302,13 @@
if (buffer == nullptr) {
return AAUDIO_ERROR_NULL;
}
+
+ // Don't allow writes when playing with a callback.
+ if (audioStream->getDataCallbackProc() != nullptr && audioStream->isPlaying()) {
+ ALOGE("Cannot write to a callback stream when running.");
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
if (numFrames < 0) {
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
} else if (numFrames == 0) {
@@ -297,6 +330,9 @@
aaudio_audio_thread_proc_t threadProc, void *arg)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ if (audioStream->getDataCallbackProc() != nullptr) {
+ return AAUDIO_ERROR_INCOMPATIBLE;
+ }
return audioStream->createThread(periodNanoseconds, threadProc, arg);
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index b054d94..68579fd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -28,7 +28,9 @@
using namespace aaudio;
-AudioStream::AudioStream() {
+AudioStream::AudioStream()
+ : mCallbackEnabled(false)
+{
// mThread is a pthread_t of unknown size so we need memset.
memset(&mThread, 0, sizeof(mThread));
setPeriodNanoseconds(0);
@@ -36,13 +38,30 @@
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
{
- // TODO validate parameters.
+
// Copy parameters from the Builder because the Builder may be deleted after this call.
mSamplesPerFrame = builder.getSamplesPerFrame();
mSampleRate = builder.getSampleRate();
mDeviceId = builder.getDeviceId();
mFormat = builder.getFormat();
- mSharingMode = builder.getSharingMode();
+ mDirection = builder.getDirection();
+
+ // callbacks
+ mFramesPerDataCallback = builder.getFramesPerDataCallback();
+ mDataCallbackProc = builder.getDataCallbackProc();
+ mErrorCallbackProc = builder.getErrorCallbackProc();
+ mDataCallbackUserData = builder.getDataCallbackUserData();
+
+ // TODO validate more parameters.
+ if (mErrorCallbackProc != nullptr && mDataCallbackProc == nullptr) {
+ ALOGE("AudioStream::open(): disconnect callback cannot be used without a data callback.");
+ return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ }
+ if (mDirection != AAUDIO_DIRECTION_INPUT && mDirection != AAUDIO_DIRECTION_OUTPUT) {
+ ALOGE("AudioStream::open(): illegal direction %d", mDirection);
+ return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ }
+
return AAUDIO_OK;
}
@@ -75,8 +94,13 @@
aaudio_stream_state_t *nextState,
int64_t timeoutNanoseconds)
{
+ aaudio_result_t result = updateStateWhileWaiting();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
// TODO replace this when similar functionality added to AudioTrack.cpp
- int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND;
+ int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND; // arbitrary
aaudio_stream_state_t state = getState();
while (state == currentState && timeoutNanoseconds > 0) {
if (durationNanos > timeoutNanoseconds) {
@@ -85,7 +109,7 @@
AudioClock::sleepForNanos(durationNanos);
timeoutNanoseconds -= durationNanos;
- aaudio_result_t result = updateState();
+ aaudio_result_t result = updateStateWhileWaiting();
if (result != AAUDIO_OK) {
return result;
}
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 6ac8554..1485d20 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -18,8 +18,8 @@
#define AAUDIO_AUDIOSTREAM_H
#include <atomic>
+#include <mutex>
#include <stdint.h>
-#include <aaudio/AAudioDefinitions.h>
#include <aaudio/AAudio.h>
#include "AAudioUtilities.h"
@@ -55,14 +55,18 @@
int64_t *timeNanoseconds) = 0;
- virtual aaudio_result_t updateState() = 0;
+ /**
+ * Update state while in the middle of waitForStateChange()
+ * @return
+ */
+ virtual aaudio_result_t updateStateWhileWaiting() = 0;
// =========== End ABSTRACT methods ===========================
virtual aaudio_result_t waitForStateChange(aaudio_stream_state_t currentState,
- aaudio_stream_state_t *nextState,
- int64_t timeoutNanoseconds);
+ aaudio_stream_state_t *nextState,
+ int64_t timeoutNanoseconds);
/**
* Open the stream using the parameters in the builder.
@@ -152,10 +156,16 @@
return mDirection;
}
+ /**
+ * This is only valid after setSamplesPerFrame() and setFormat() have been called.
+ */
int32_t getBytesPerFrame() const {
return mSamplesPerFrame * getBytesPerSample();
}
+ /**
+ * This is only valid after setFormat() has been called.
+ */
int32_t getBytesPerSample() const {
return AAudioConvert_formatToSizeInBytes(mFormat);
}
@@ -168,6 +178,27 @@
return mFramesRead.get();
}
+ AAudioStream_dataCallback getDataCallbackProc() const {
+ return mDataCallbackProc;
+ }
+ AAudioStream_errorCallback getErrorCallbackProc() const {
+ return mErrorCallbackProc;
+ }
+
+ void *getDataCallbackUserData() const {
+ return mDataCallbackUserData;
+ }
+ void *getErrorCallbackUserData() const {
+ return mErrorCallbackUserData;
+ }
+
+ int32_t getFramesPerDataCallback() const {
+ return mFramesPerDataCallback;
+ }
+
+ bool isDataCallbackActive() {
+ return (mDataCallbackProc != nullptr) && isPlaying();
+ }
// ============== I/O ===========================
// A Stream will only implement read() or write() depending on its direction.
@@ -235,6 +266,9 @@
mState = state;
}
+ std::mutex mStreamMutex;
+
+ std::atomic<bool> mCallbackEnabled;
protected:
@@ -259,6 +293,15 @@
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+ // callback ----------------------------------
+
+ AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
+ void *mDataCallbackUserData = nullptr;
+ int32_t mFramesPerDataCallback = AAUDIO_UNSPECIFIED; // frames
+
+ AAudioStream_errorCallback mErrorCallbackProc = nullptr;
+ void *mErrorCallbackUserData = nullptr;
+
// background thread ----------------------------------
bool mHasThread = false;
pthread_t mThread; // initialized in constructor
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 5a54e62..858ae80 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -44,6 +44,7 @@
aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {
AudioStream* audioStream = nullptr;
const aaudio_sharing_mode_t sharingMode = getSharingMode();
+ ALOGE("AudioStreamBuilder.build() sharingMode = %d", sharingMode);
switch (getDirection()) {
case AAUDIO_DIRECTION_INPUT:
switch (sharingMode) {
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 7b5f35c..93ca7f5 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_AUDIOSTREAMBUILDER_H
-#define AAUDIO_AUDIOSTREAMBUILDER_H
+#ifndef AAUDIO_AUDIO_STREAM_BUILDER_H
+#define AAUDIO_AUDIO_STREAM_BUILDER_H
#include <stdint.h>
@@ -101,6 +101,52 @@
return this;
}
+ AAudioStream_dataCallback getDataCallbackProc() const {
+ return mDataCallbackProc;
+ }
+
+ AudioStreamBuilder* setDataCallbackProc(AAudioStream_dataCallback proc) {
+ mDataCallbackProc = proc;
+ return this;
+ }
+
+
+ void *getDataCallbackUserData() const {
+ return mDataCallbackUserData;
+ }
+
+ AudioStreamBuilder* setDataCallbackUserData(void *userData) {
+ mDataCallbackUserData = userData;
+ return this;
+ }
+
+ AAudioStream_errorCallback getErrorCallbackProc() const {
+ return mErrorCallbackProc;
+ }
+
+ AudioStreamBuilder* setErrorCallbackProc(AAudioStream_errorCallback proc) {
+ mErrorCallbackProc = proc;
+ return this;
+ }
+
+ AudioStreamBuilder* setErrorCallbackUserData(void *userData) {
+ mErrorCallbackUserData = userData;
+ return this;
+ }
+
+ void *getErrorCallbackUserData() const {
+ return mErrorCallbackUserData;
+ }
+
+ int32_t getFramesPerDataCallback() const {
+ return mFramesPerDataCallback;
+ }
+
+ AudioStreamBuilder* setFramesPerDataCallback(int32_t sizeInFrames) {
+ mFramesPerDataCallback = sizeInFrames;
+ return this;
+ }
+
aaudio_result_t build(AudioStream **streamPtr);
private:
@@ -111,8 +157,15 @@
aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+
+ AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
+ void *mDataCallbackUserData = nullptr;
+ int32_t mFramesPerDataCallback = AAUDIO_UNSPECIFIED; // frames
+
+ AAudioStream_errorCallback mErrorCallbackProc = nullptr;
+ void *mErrorCallbackUserData = nullptr;
};
} /* namespace aaudio */
-#endif /* AAUDIO_AUDIOSTREAMBUILDER_H */
+#endif //AAUDIO_AUDIO_STREAM_BUILDER_H
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
new file mode 100644
index 0000000..baa24c9
--- /dev/null
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioStreamLegacy"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <utils/String16.h>
+#include <media/AudioTrack.h>
+#include <aaudio/AAudio.h>
+
+#include "core/AudioStream.h"
+#include "legacy/AudioStreamLegacy.h"
+
+using namespace android;
+using namespace aaudio;
+
+AudioStreamLegacy::AudioStreamLegacy()
+ : AudioStream() {
+}
+
+AudioStreamLegacy::~AudioStreamLegacy() {
+}
+
+// Called from AudioTrack.cpp or AudioRecord.cpp
+static void AudioStreamLegacy_callback(int event, void* userData, void *info) {
+ AudioStreamLegacy *streamLegacy = (AudioStreamLegacy *) userData;
+ streamLegacy->processCallback(event, info);
+}
+
+aaudio_legacy_callback_t AudioStreamLegacy::getLegacyCallback() {
+ return AudioStreamLegacy_callback;
+}
+
+// Implement FixedBlockProcessor
+int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
+ int32_t frameCount = numBytes / getBytesPerFrame();
+ // Call using the AAudio callback interface.
+ AAudioStream_dataCallback appCallback = getDataCallbackProc();
+ return (*appCallback)(
+ (AAudioStream *) this,
+ getDataCallbackUserData(),
+ buffer,
+ frameCount);
+}
+
+void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
+ aaudio_data_callback_result_t callbackResult;
+ switch (opcode) {
+ case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
+ // Note that this code assumes an AudioTrack::Buffer is the same as AudioRecord::Buffer
+ // TODO define our own AudioBuffer and pass it from the subclasses.
+ AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
+ if (audioBuffer->frameCount == 0) return;
+
+ // If the caller specified an exact size then use a block size adapter.
+ if (mBlockAdapter != nullptr) {
+ int32_t byteCount = audioBuffer->frameCount * getBytesPerFrame();
+ callbackResult = mBlockAdapter->processVariableBlock((uint8_t *) audioBuffer->raw,
+ byteCount);
+ } else {
+ // Call using the AAudio callback interface.
+ callbackResult = (*getDataCallbackProc())(
+ (AAudioStream *) this,
+ getDataCallbackUserData(),
+ audioBuffer->raw,
+ audioBuffer->frameCount
+ );
+ }
+ if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
+ audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
+ } else {
+ audioBuffer->size = 0;
+ }
+ }
+ break;
+
+ // Stream got rerouted so we disconnect.
+ case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
+ ALOGD("AudioStreamAAudio(): callbackLoop() stream disconnected");
+ if (getErrorCallbackProc() != nullptr) {
+ (*getErrorCallbackProc())(
+ (AAudioStream *) this,
+ getErrorCallbackUserData(),
+ AAUDIO_OK
+ );
+ }
+ mCallbackEnabled.store(false);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
new file mode 100644
index 0000000..c109ee7
--- /dev/null
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LEGACY_AUDIO_STREAM_LEGACY_H
+#define LEGACY_AUDIO_STREAM_LEGACY_H
+
+
+#include <aaudio/AAudio.h>
+
+#include "AudioStream.h"
+#include "AAudioLegacy.h"
+#include "utility/FixedBlockAdapter.h"
+
+namespace aaudio {
+
+
+typedef void (*aaudio_legacy_callback_t)(int event, void* user, void *info);
+
+enum {
+ /**
+ * Request that the callback function should fill the data buffer of an output stream,
+ * or process the data of an input stream.
+ * The address parameter passed to the callback function will point to a data buffer.
+ * For an input stream, the data is read-only.
+ * The value1 parameter will be the number of frames.
+ * The value2 parameter is reserved and will be set to zero.
+ * The callback should return AAUDIO_CALLBACK_RESULT_CONTINUE or AAUDIO_CALLBACK_RESULT_STOP.
+ */
+ AAUDIO_CALLBACK_OPERATION_PROCESS_DATA,
+
+ /**
+ * Inform the callback function that the stream was disconnected.
+ * The address parameter passed to the callback function will be NULL.
+ * The value1 will be an error code or AAUDIO_OK.
+ * The value2 parameter is reserved and will be set to zero.
+ * The callback return value will be ignored.
+ */
+ AAUDIO_CALLBACK_OPERATION_DISCONNECTED,
+};
+typedef int32_t aaudio_callback_operation_t;
+
+
+class AudioStreamLegacy : public AudioStream, public FixedBlockProcessor {
+public:
+ AudioStreamLegacy();
+
+ virtual ~AudioStreamLegacy();
+
+ aaudio_legacy_callback_t getLegacyCallback();
+
+ // This is public so it can be called from the C callback function.
+ // This is called from the AudioTrack/AudioRecord client.
+ virtual void processCallback(int event, void *info) = 0;
+
+ void processCallbackCommon(aaudio_callback_operation_t opcode, void *info);
+
+ // Implement FixedBlockProcessor
+ int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override;
+
+protected:
+ FixedBlockAdapter *mBlockAdapter = nullptr;
+ aaudio_wrapping_frames_t mPositionWhenStarting = 0;
+ int32_t mCallbackBufferSize = 0;
+};
+
+} /* namespace aaudio */
+
+#endif //LEGACY_AUDIO_STREAM_LEGACY_H
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 78c68ae..f0a6ceb 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -24,14 +24,16 @@
#include <aaudio/AAudio.h>
#include "AudioClock.h"
-#include "AudioStreamRecord.h"
-#include "utility/AAudioUtilities.h"
+#include "legacy/AudioStreamLegacy.h"
+#include "legacy/AudioStreamRecord.h"
+#include "utility/FixedBlockWriter.h"
using namespace android;
using namespace aaudio;
AudioStreamRecord::AudioStreamRecord()
- : AudioStream()
+ : AudioStreamLegacy()
+ , mFixedBlockWriter(*this)
{
}
@@ -58,7 +60,6 @@
? 2 : getSamplesPerFrame();
audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
- AudioRecord::callback_t callback = nullptr;
audio_input_flags_t flags = (audio_input_flags_t) AUDIO_INPUT_FLAG_NONE;
size_t frameCount = (builder.getBufferCapacity() == AAUDIO_UNSPECIFIED) ? 0
@@ -68,6 +69,17 @@
? AUDIO_FORMAT_PCM_FLOAT
: AAudioConvert_aaudioToAndroidDataFormat(getFormat());
+ // Setup the callback if there is one.
+ AudioRecord::callback_t callback = nullptr;
+ void *callbackData = nullptr;
+ AudioRecord::transfer_type streamTransferType = AudioRecord::transfer_type::TRANSFER_SYNC;
+ if (builder.getDataCallbackProc() != nullptr) {
+ streamTransferType = AudioRecord::transfer_type::TRANSFER_CALLBACK;
+ callback = getLegacyCallback();
+ callbackData = this;
+ }
+ mCallbackBufferSize = builder.getFramesPerDataCallback();
+
mAudioRecord = new AudioRecord(
AUDIO_SOURCE_DEFAULT,
getSampleRate(),
@@ -76,10 +88,10 @@
mOpPackageName, // const String16& opPackageName TODO does not compile
frameCount,
callback,
- nullptr, // void* user = nullptr,
+ callbackData,
0, // uint32_t notificationFrames = 0,
AUDIO_SESSION_ALLOCATE,
- AudioRecord::TRANSFER_DEFAULT,
+ streamTransferType,
flags
// int uid = -1,
// pid_t pid = -1,
@@ -99,6 +111,15 @@
setSamplesPerFrame(mAudioRecord->channelCount());
setFormat(AAudioConvert_androidToAAudioDataFormat(mAudioRecord->format()));
+ // We may need to pass the data through a block size adapter to guarantee constant size.
+ if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
+ int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+ mFixedBlockWriter.open(callbackSizeBytes);
+ mBlockAdapter = &mFixedBlockWriter;
+ } else {
+ mBlockAdapter = nullptr;
+ }
+
setState(AAUDIO_STREAM_STATE_OPEN);
return AAUDIO_OK;
@@ -111,9 +132,29 @@
mAudioRecord.clear();
setState(AAUDIO_STREAM_STATE_CLOSED);
}
+ mFixedBlockWriter.close();
return AAUDIO_OK;
}
+void AudioStreamRecord::processCallback(int event, void *info) {
+
+ ALOGD("AudioStreamRecord::processCallback(), event %d", event);
+ switch (event) {
+ case AudioRecord::EVENT_MORE_DATA:
+ processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info);
+ break;
+
+ // Stream got rerouted so we disconnect.
+ case AudioRecord::EVENT_NEW_IAUDIORECORD:
+ processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
+ break;
+
+ default:
+ break;
+ }
+ return;
+}
+
aaudio_result_t AudioStreamRecord::requestStart()
{
if (mAudioRecord.get() == nullptr) {
@@ -124,6 +165,7 @@
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
}
+
err = mAudioRecord->start();
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
@@ -151,7 +193,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamRecord::updateState()
+aaudio_result_t AudioStreamRecord::updateStateWhileWaiting()
{
aaudio_result_t result = AAUDIO_OK;
aaudio_wrapping_frames_t position;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 4667f05..897a5b3 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -23,51 +23,58 @@
#include "AudioStreamBuilder.h"
#include "AudioStream.h"
#include "AAudioLegacy.h"
+#include "legacy/AudioStreamLegacy.h"
+#include "utility/FixedBlockWriter.h"
namespace aaudio {
/**
* Internal stream that uses the legacy AudioTrack path.
*/
-class AudioStreamRecord : public AudioStream {
+class AudioStreamRecord : public AudioStreamLegacy {
public:
AudioStreamRecord();
virtual ~AudioStreamRecord();
- virtual aaudio_result_t open(const AudioStreamBuilder & builder) override;
- virtual aaudio_result_t close() override;
+ aaudio_result_t open(const AudioStreamBuilder & builder) override;
+ aaudio_result_t close() override;
- virtual aaudio_result_t requestStart() override;
- virtual aaudio_result_t requestPause() override;
- virtual aaudio_result_t requestFlush() override;
- virtual aaudio_result_t requestStop() override;
+ aaudio_result_t requestStart() override;
+ aaudio_result_t requestPause() override;
+ aaudio_result_t requestFlush() override;
+ aaudio_result_t requestStop() override;
virtual aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t read(void *buffer,
+ aaudio_result_t read(void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
- virtual aaudio_result_t setBufferSize(int32_t requestedFrames) override;
+ aaudio_result_t setBufferSize(int32_t requestedFrames) override;
- virtual int32_t getBufferSize() const override;
+ int32_t getBufferSize() const override;
- virtual int32_t getBufferCapacity() const override;
+ int32_t getBufferCapacity() const override;
- virtual int32_t getXRunCount() const override;
+ int32_t getXRunCount() const override;
- virtual int32_t getFramesPerBurst() const override;
+ int32_t getFramesPerBurst() const override;
- virtual aaudio_result_t updateState() override;
+ aaudio_result_t updateStateWhileWaiting() override;
+
+ // This is public so it can be called from the C callback function.
+ void processCallback(int event, void *info) override;
private:
android::sp<android::AudioRecord> mAudioRecord;
+ // adapts between variable sized blocks and fixed size blocks
+ FixedBlockWriter mFixedBlockWriter;
+
// TODO add 64-bit position reporting to AudioRecord and use it.
- aaudio_wrapping_frames_t mPositionWhenStarting = 0;
- android::String16 mOpPackageName;
+ android::String16 mOpPackageName;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index a7c0677..ff87c28 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -20,20 +20,25 @@
#include <stdint.h>
#include <media/AudioTrack.h>
-#include <aaudio/AAudio.h>
-#include "utility/AudioClock.h"
-#include "AudioStreamTrack.h"
-#include "utility/AAudioUtilities.h"
+#include <aaudio/AAudio.h>
+#include "AudioClock.h"
+#include "legacy/AudioStreamLegacy.h"
+#include "legacy/AudioStreamTrack.h"
+#include "utility/FixedBlockReader.h"
using namespace android;
using namespace aaudio;
+// Arbitrary and somewhat generous number of bursts.
+#define DEFAULT_BURSTS_PER_BUFFER_CAPACITY 8
+
/*
* Create a stream that uses the AudioTrack.
*/
AudioStreamTrack::AudioStreamTrack()
- : AudioStream()
+ : AudioStreamLegacy()
+ , mFixedBlockReader(*this)
{
}
@@ -53,6 +58,8 @@
return result;
}
+ ALOGD("AudioStreamTrack::open = %p", this);
+
// Try to create an AudioTrack
// TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
@@ -61,16 +68,40 @@
ALOGD("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
samplesPerFrame, channelMask);
- AudioTrack::callback_t callback = nullptr;
// TODO add more performance options
audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
- size_t frameCount = (builder.getBufferCapacity() == AAUDIO_UNSPECIFIED) ? 0
- : builder.getBufferCapacity();
+
+ int32_t frameCount = builder.getBufferCapacity();
+ ALOGD("AudioStreamTrack::open(), requested buffer capacity %d", frameCount);
+
+ int32_t notificationFrames = 0;
+
// TODO implement an unspecified AudioTrack format then use that.
- audio_format_t format = (getFormat() == AAUDIO_UNSPECIFIED)
+ audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
? AUDIO_FORMAT_PCM_FLOAT
: AAudioConvert_aaudioToAndroidDataFormat(getFormat());
+ // Setup the callback if there is one.
+ AudioTrack::callback_t callback = nullptr;
+ void *callbackData = nullptr;
+ // Note that TRANSFER_SYNC does not allow FAST track
+ AudioTrack::transfer_type streamTransferType = AudioTrack::transfer_type::TRANSFER_SYNC;
+ if (builder.getDataCallbackProc() != nullptr) {
+ streamTransferType = AudioTrack::transfer_type::TRANSFER_CALLBACK;
+ callback = getLegacyCallback();
+ callbackData = this;
+
+ notificationFrames = builder.getFramesPerDataCallback();
+ // If the total buffer size is unspecified then base the size on the burst size.
+ if (frameCount == AAUDIO_UNSPECIFIED) {
+ // Take advantage of a special trick that allows us to create a buffer
+ // that is some multiple of the burst size.
+ notificationFrames = 0 - DEFAULT_BURSTS_PER_BUFFER_CAPACITY;
+ }
+ }
+ mCallbackBufferSize = builder.getFramesPerDataCallback();
+
+ ALOGD("AudioStreamTrack::open(), notificationFrames = %d", notificationFrames);
mAudioTrack = new AudioTrack(
(audio_stream_type_t) AUDIO_STREAM_MUSIC,
getSampleRate(),
@@ -79,10 +110,10 @@
frameCount,
flags,
callback,
- nullptr, // user callback data
- 0, // notificationFrames
+ callbackData,
+ notificationFrames,
AUDIO_SESSION_ALLOCATE,
- AudioTrack::transfer_type::TRANSFER_SYNC // TODO - this does not allow FAST
+ streamTransferType
);
// Did we get a valid track?
@@ -97,7 +128,18 @@
// Get the actual values from the AudioTrack.
setSamplesPerFrame(mAudioTrack->channelCount());
setSampleRate(mAudioTrack->getSampleRate());
- setFormat(AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format()));
+ aaudio_audio_format_t aaudioFormat =
+ AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format());
+ setFormat(aaudioFormat);
+
+ // We may need to pass the data through a block size adapter to guarantee constant size.
+ if (mCallbackBufferSize != AAUDIO_UNSPECIFIED) {
+ int callbackSizeBytes = getBytesPerFrame() * mCallbackBufferSize;
+ mFixedBlockReader.open(callbackSizeBytes);
+ mBlockAdapter = &mFixedBlockReader;
+ } else {
+ mBlockAdapter = nullptr;
+ }
setState(AAUDIO_STREAM_STATE_OPEN);
@@ -111,11 +153,32 @@
mAudioTrack.clear(); // TODO is this right?
setState(AAUDIO_STREAM_STATE_CLOSED);
}
+ mFixedBlockReader.close();
return AAUDIO_OK;
}
+void AudioStreamTrack::processCallback(int event, void *info) {
+
+ switch (event) {
+ case AudioTrack::EVENT_MORE_DATA:
+ processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info);
+ break;
+
+ // Stream got rerouted so we disconnect.
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+ processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
+ break;
+
+ default:
+ break;
+ }
+ return;
+}
+
aaudio_result_t AudioStreamTrack::requestStart()
{
+ std::lock_guard<std::mutex> lock(mStreamMutex);
+
if (mAudioTrack.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -124,6 +187,7 @@
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
}
+
err = mAudioTrack->start();
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
@@ -135,11 +199,14 @@
aaudio_result_t AudioStreamTrack::requestPause()
{
+ std::lock_guard<std::mutex> lock(mStreamMutex);
+
if (mAudioTrack.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_STARTING
&& getState() != AAUDIO_STREAM_STATE_STARTED) {
- ALOGE("requestPause(), called when state is %s", AAudio_convertStreamStateToText(getState()));
+ ALOGE("requestPause(), called when state is %s",
+ AAudio_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_PAUSING);
@@ -152,6 +219,8 @@
}
aaudio_result_t AudioStreamTrack::requestFlush() {
+ std::lock_guard<std::mutex> lock(mStreamMutex);
+
if (mAudioTrack.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
@@ -165,6 +234,8 @@
}
aaudio_result_t AudioStreamTrack::requestStop() {
+ std::lock_guard<std::mutex> lock(mStreamMutex);
+
if (mAudioTrack.get() == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -175,7 +246,7 @@
return AAUDIO_OK;
}
-aaudio_result_t AudioStreamTrack::updateState()
+aaudio_result_t AudioStreamTrack::updateStateWhileWaiting()
{
status_t err;
aaudio_wrapping_frames_t position;
@@ -303,7 +374,7 @@
}
// TODO Merge common code into AudioStreamLegacy after rebasing.
int timebase;
- switch(clockId) {
+ switch (clockId) {
case CLOCK_BOOTTIME:
timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
break;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 7a53022..29f5d15 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -17,54 +17,63 @@
#ifndef LEGACY_AUDIO_STREAM_TRACK_H
#define LEGACY_AUDIO_STREAM_TRACK_H
+#include <math.h>
#include <media/AudioTrack.h>
#include <aaudio/AAudio.h>
#include "AudioStreamBuilder.h"
#include "AudioStream.h"
-#include "AAudioLegacy.h"
+#include "legacy/AAudioLegacy.h"
+#include "legacy/AudioStreamLegacy.h"
+#include "utility/FixedBlockReader.h"
namespace aaudio {
-
/**
* Internal stream that uses the legacy AudioTrack path.
*/
-class AudioStreamTrack : public AudioStream {
+class AudioStreamTrack : public AudioStreamLegacy {
public:
AudioStreamTrack();
virtual ~AudioStreamTrack();
- virtual aaudio_result_t open(const AudioStreamBuilder & builder) override;
- virtual aaudio_result_t close() override;
+ aaudio_result_t open(const AudioStreamBuilder & builder) override;
+ aaudio_result_t close() override;
- virtual aaudio_result_t requestStart() override;
- virtual aaudio_result_t requestPause() override;
- virtual aaudio_result_t requestFlush() override;
- virtual aaudio_result_t requestStop() override;
+ aaudio_result_t requestStart() override;
+ aaudio_result_t requestPause() override;
+ aaudio_result_t requestFlush() override;
+ aaudio_result_t requestStop() override;
- virtual aaudio_result_t getTimestamp(clockid_t clockId,
+ aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t write(const void *buffer,
+ aaudio_result_t write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
- virtual aaudio_result_t setBufferSize(int32_t requestedFrames) override;
- virtual int32_t getBufferSize() const override;
- virtual int32_t getBufferCapacity() const override;
- virtual int32_t getFramesPerBurst()const override;
- virtual int32_t getXRunCount() const override;
+ aaudio_result_t setBufferSize(int32_t requestedFrames) override;
+ int32_t getBufferSize() const override;
+ int32_t getBufferCapacity() const override;
+ int32_t getFramesPerBurst()const override;
+ int32_t getXRunCount() const override;
- virtual int64_t getFramesRead() override;
+ int64_t getFramesRead() override;
- virtual aaudio_result_t updateState() override;
+ aaudio_result_t updateStateWhileWaiting() override;
+
+ // This is public so it can be called from the C callback function.
+ void processCallback(int event, void *info) override;
private:
+
android::sp<android::AudioTrack> mAudioTrack;
+ // adapts between variable sized blocks and fixed size blocks
+ FixedBlockReader mFixedBlockReader;
+
// TODO add 64-bit position reporting to AudioRecord and use it.
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
aaudio_wrapping_frames_t mPositionWhenPausing = 0;
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.cpp b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
new file mode 100644
index 0000000..f4666af
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+FixedBlockAdapter::~FixedBlockAdapter() {
+ close();
+}
+
+int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock)
+{
+ mSize = bytesPerFixedBlock;
+ mStorage = new uint8_t[bytesPerFixedBlock]; // TODO use std::nothrow
+ mPosition = 0;
+ return 0;
+}
+
+int32_t FixedBlockAdapter::close()
+{
+ delete[] mStorage;
+ mStorage = nullptr;
+ mSize = 0;
+ mPosition = 0;
+ return 0;
+}
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.h b/media/libaaudio/src/utility/FixedBlockAdapter.h
new file mode 100644
index 0000000..7008b25
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_ADAPTER_H
+#define AAUDIO_FIXED_BLOCK_ADAPTER_H
+
+#include <stdio.h>
+
+/**
+ * Interface for a class that needs fixed-size blocks.
+ */
+class FixedBlockProcessor {
+public:
+ virtual ~FixedBlockProcessor() = default;
+ virtual int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) = 0;
+};
+
+/**
+ * Base class for a variable-to-fixed-size block adapter.
+ */
+class FixedBlockAdapter
+{
+public:
+ FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor)
+ : mFixedBlockProcessor(fixedBlockProcessor) {}
+
+ virtual ~FixedBlockAdapter();
+
+ /**
+ * Allocate internal resources needed for buffering data.
+ */
+ virtual int32_t open(int32_t bytesPerFixedBlock);
+
+ /**
+ * Note that if the fixed-sized blocks must be aligned, then the variable-sized blocks
+ * must have the same alignment.
+ * For example, if the fixed-size blocks must be a multiple of 8, then the variable-sized
+ * blocks must also be a multiple of 8.
+ *
+ * @param buffer
+ * @param numBytes
+ * @return zero if OK or a non-zero code
+ */
+ virtual int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) = 0;
+
+ /**
+ * Free internal resources.
+ */
+ int32_t close();
+
+protected:
+ FixedBlockProcessor &mFixedBlockProcessor;
+ uint8_t *mStorage = nullptr; // Store data here while assembling buffers.
+ int32_t mSize = 0; // Size in bytes of the fixed size buffer.
+ int32_t mPosition = 0; // Offset of the last byte read or written.
+};
+
+#endif /* AAUDIO_FIXED_BLOCK_ADAPTER_H */
diff --git a/media/libaaudio/src/utility/FixedBlockReader.cpp b/media/libaaudio/src/utility/FixedBlockReader.cpp
new file mode 100644
index 0000000..21ea70e
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockReader.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <memory.h>
+
+#include "FixedBlockAdapter.h"
+
+#include "FixedBlockReader.h"
+
+
+FixedBlockReader::FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor)
+ : FixedBlockAdapter(fixedBlockProcessor) {
+ mPosition = mSize;
+}
+
+int32_t FixedBlockReader::open(int32_t bytesPerFixedBlock) {
+ int32_t result = FixedBlockAdapter::open(bytesPerFixedBlock);
+ mPosition = mSize; // Indicate no data in storage.
+ return result;
+}
+
+int32_t FixedBlockReader::readFromStorage(uint8_t *buffer, int32_t numBytes) {
+ int32_t bytesToRead = numBytes;
+ int32_t dataAvailable = mSize - mPosition;
+ if (bytesToRead > dataAvailable) {
+ bytesToRead = dataAvailable;
+ }
+ memcpy(buffer, mStorage + mPosition, bytesToRead);
+ mPosition += bytesToRead;
+ return bytesToRead;
+}
+
+int32_t FixedBlockReader::processVariableBlock(uint8_t *buffer, int32_t numBytes) {
+ int32_t result = 0;
+ int32_t bytesLeft = numBytes;
+ while(bytesLeft > 0 && result == 0) {
+ if (mPosition < mSize) {
+ // Use up bytes currently in storage.
+ int32_t bytesRead = readFromStorage(buffer, bytesLeft);
+ buffer += bytesRead;
+ bytesLeft -= bytesRead;
+ } else if (bytesLeft >= mSize) {
+ // Read through if enough for a complete block.
+ result = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize);
+ buffer += mSize;
+ bytesLeft -= mSize;
+ } else {
+ // Just need a partial block so we have to use storage.
+ result = mFixedBlockProcessor.onProcessFixedBlock(mStorage, mSize);
+ mPosition = 0;
+ }
+ }
+ return result;
+}
+
diff --git a/media/libaaudio/src/utility/FixedBlockReader.h b/media/libaaudio/src/utility/FixedBlockReader.h
new file mode 100644
index 0000000..128dd52
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockReader.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_READER_H
+#define AAUDIO_FIXED_BLOCK_READER_H
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+/**
+ * Read from a fixed-size block to a variable sized block.
+ *
+ * This can be used to convert a pull data flow from fixed sized buffers to variable sized buffers.
+ * An example would be an audio output callback that reads from the app.
+ */
+class FixedBlockReader : public FixedBlockAdapter
+{
+public:
+ FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor);
+
+ virtual ~FixedBlockReader() = default;
+
+ int32_t open(int32_t bytesPerFixedBlock) override;
+
+ int32_t readFromStorage(uint8_t *buffer, int32_t numBytes);
+
+ /**
+ * Read into a variable sized block.
+ */
+ int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) override;
+};
+
+
+#endif /* AAUDIO_FIXED_BLOCK_READER_H */
diff --git a/media/libaaudio/src/utility/FixedBlockWriter.cpp b/media/libaaudio/src/utility/FixedBlockWriter.cpp
new file mode 100644
index 0000000..2ce8046
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockWriter.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <memory.h>
+
+#include "FixedBlockAdapter.h"
+#include "FixedBlockWriter.h"
+
+FixedBlockWriter::FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor)
+ : FixedBlockAdapter(fixedBlockProcessor) {}
+
+
+int32_t FixedBlockWriter::writeToStorage(uint8_t *buffer, int32_t numBytes) {
+ int32_t bytesToStore = numBytes;
+ int32_t roomAvailable = mSize - mPosition;
+ if (bytesToStore > roomAvailable) {
+ bytesToStore = roomAvailable;
+ }
+ memcpy(mStorage + mPosition, buffer, bytesToStore);
+ mPosition += bytesToStore;
+ return bytesToStore;
+}
+
+int32_t FixedBlockWriter::processVariableBlock(uint8_t *buffer, int32_t numBytes) {
+ int32_t result = 0;
+ int32_t bytesLeft = numBytes;
+
+ // If we already have data in storage then add to it.
+ if (mPosition > 0) {
+ int32_t bytesWritten = writeToStorage(buffer, bytesLeft);
+ buffer += bytesWritten;
+ bytesLeft -= bytesWritten;
+ // If storage full then flush it out
+ if (mPosition == mSize) {
+ result = mFixedBlockProcessor.onProcessFixedBlock(mStorage, mSize);
+ mPosition = 0;
+ }
+ }
+
+ // Write through if enough for a complete block.
+ while(bytesLeft > mSize && result == 0) {
+ result = mFixedBlockProcessor.onProcessFixedBlock(buffer, mSize);
+ buffer += mSize;
+ bytesLeft -= mSize;
+ }
+
+ // Save any remaining partial block for next time.
+ if (bytesLeft > 0) {
+ writeToStorage(buffer, bytesLeft);
+ }
+
+ return result;
+}
diff --git a/media/libaaudio/src/utility/FixedBlockWriter.h b/media/libaaudio/src/utility/FixedBlockWriter.h
new file mode 100644
index 0000000..f1d917c
--- /dev/null
+++ b/media/libaaudio/src/utility/FixedBlockWriter.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_FIXED_BLOCK_WRITER_H
+#define AAUDIO_FIXED_BLOCK_WRITER_H
+
+#include <stdint.h>
+
+#include "FixedBlockAdapter.h"
+
+/**
+ * This can be used to convert a push data flow from variable sized buffers to fixed sized buffers.
+ * An example would be an audio input callback.
+ */
+class FixedBlockWriter : public FixedBlockAdapter
+{
+public:
+ FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor);
+
+ virtual ~FixedBlockWriter() = default;
+
+ int32_t writeToStorage(uint8_t *buffer, int32_t numBytes);
+
+ /**
+ * Write from a variable sized block.
+ */
+ int32_t processVariableBlock(uint8_t *buffer, int32_t numBytes) override;
+};
+
+#endif /* AAUDIO_FIXED_BLOCK_WRITER_H */