aaudio: use new flowgraph to simplify processing
Construct a flowgraph based on the source and destination
format and channelCount. This is groundwork for supporting 24-bit
PCM formats.
Also cleaned up handling of device related format.
This CL removes more code than it adds.
Bug: 65067568
Test: write_sine_callback.cpp -pl
Test: write_sine_callback.cpp -pl -x
Test: input_monitor -pl
Test: input_monitor -pl -x
Change-Id: Ia155bff0164912011d09b61b54f983ccf4490bd1
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
new file mode 100644
index 0000000..3e43c6b
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 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 "AAudioFlowGraph"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AAudioFlowGraph.h"
+
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+#include <flowgraph/SinkFloat.h>
+#include <flowgraph/SinkI16.h>
+#include <flowgraph/SinkI24.h>
+#include <flowgraph/SourceFloat.h>
+#include <flowgraph/SourceI16.h>
+#include <flowgraph/SourceI24.h>
+
+using namespace flowgraph;
+
+aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
+ int32_t sourceChannelCount,
+ audio_format_t sinkFormat,
+ int32_t sinkChannelCount) {
+ AudioFloatOutputPort *lastOutput = nullptr;
+
+ ALOGD("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
+ __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
+
+ switch (sourceFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ mSource = std::make_unique<SourceFloat>(sourceChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ mSource = std::make_unique<SourceI16>(sourceChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ mSource = std::make_unique<SourceI24>(sourceChannelCount);
+ break;
+ default: // TODO add I32
+ ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+ lastOutput = &mSource->output;
+
+ // Apply volume as a ramp to avoid pops.
+ mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
+ lastOutput->connect(&mVolumeRamp->input);
+ lastOutput = &mVolumeRamp->output;
+
+ // For a pure float graph, there is chance that the data range may be very large.
+ // So we should clip to a reasonable value that allows a little headroom.
+ if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ mClipper = std::make_unique<ClipToRange>(sourceChannelCount);
+ lastOutput->connect(&mClipper->input);
+ lastOutput = &mClipper->output;
+ }
+
+ // Expand the number of channels if required.
+ if (sourceChannelCount == 1 && sinkChannelCount > 1) {
+ mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
+ lastOutput->connect(&mChannelConverter->input);
+ lastOutput = &mChannelConverter->output;
+ } else if (sourceChannelCount != sinkChannelCount) {
+ ALOGE("%s() Channel reduction not supported.", __func__);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
+ switch (sinkFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ mSink = std::make_unique<SinkFloat>(sinkChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ mSink = std::make_unique<SinkI16>(sinkChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ mSink = std::make_unique<SinkI24>(sinkChannelCount);
+ break;
+ default: // TODO add I32
+ ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+ lastOutput->connect(&mSink->input);
+
+ return AAUDIO_OK;
+}
+
+void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) {
+ mSource->setData(source, numFrames);
+ mSink->read(destination, numFrames);
+}
+
+/**
+ * @param volume between 0.0 and 1.0
+ */
+void AAudioFlowGraph::setTargetVolume(float volume) {
+ mVolumeRamp->setTarget(volume);
+}
+
+void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
+ mVolumeRamp->setLengthInFrames(numFrames);
+}
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
new file mode 100644
index 0000000..a49f64e
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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 ANDROID_AAUDIO_FLOW_GRAPH_H
+#define ANDROID_AAUDIO_FLOW_GRAPH_H
+
+#include <memory>
+#include <stdint.h>
+#include <sys/types.h>
+#include <system/audio.h>
+
+#include <aaudio/AAudio.h>
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+
+class AAudioFlowGraph {
+public:
+ /** Connect several modules together to convert from source to sink.
+ * This should only be called once for each instance.
+ *
+ * @param sourceFormat
+ * @param sourceChannelCount
+ * @param sinkFormat
+ * @param sinkChannelCount
+ * @return
+ */
+ aaudio_result_t configure(audio_format_t sourceFormat,
+ int32_t sourceChannelCount,
+ audio_format_t sinkFormat,
+ int32_t sinkChannelCount);
+
+ void process(const void *source, void *destination, int32_t numFrames);
+
+ /**
+ * @param volume between 0.0 and 1.0
+ */
+ void setTargetVolume(float volume);
+
+ void setRampLengthInFrames(int32_t numFrames);
+
+private:
+ std::unique_ptr<flowgraph::AudioSource> mSource;
+ std::unique_ptr<flowgraph::RampLinear> mVolumeRamp;
+ std::unique_ptr<flowgraph::ClipToRange> mClipper;
+ std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
+ std::unique_ptr<flowgraph::AudioSink> mSink;
+};
+
+
+#endif //ANDROID_AAUDIO_FLOW_GRAPH_H
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 9204824..0a8021a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -39,7 +39,6 @@
#include "core/AudioStreamBuilder.h"
#include "fifo/FifoBuffer.h"
#include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
#include "AudioStreamInternal.h"
@@ -92,11 +91,11 @@
}
// We have to do volume scaling. So we prefer FLOAT format.
- if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
- setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ setFormat(AUDIO_FORMAT_PCM_FLOAT);
}
// Request FLOAT for the shared mixer.
- request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
@@ -126,7 +125,7 @@
// if that failed then try switching from mono to stereo if OUTPUT.
// Only do this in the client. Otherwise we end up with a mono mixer in the service
// that writes to a stereo MMAP stream.
- ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO",
+ ALOGD("%s() - openStream() returned %d, try switching from MONO to STEREO",
__func__, mServiceStreamHandle);
request.getConfiguration().setSamplesPerFrame(2); // stereo
mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
@@ -212,9 +211,7 @@
mCallbackFrames = mFramesPerBurst;
}
- int32_t bytesPerFrame = getSamplesPerFrame()
- * AAudioConvert_formatToSizeInBytes(getFormat());
- int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
+ const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
mCallbackBuffer = new uint8_t[callbackBufferSize];
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0425cd5..3bb9e1e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -27,7 +27,6 @@
#include "client/AudioEndpoint.h"
#include "core/AudioStream.h"
#include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
using android::sp;
using android::IAAudioService;
@@ -193,6 +192,8 @@
int64_t mServiceLatencyNanos = 0;
+ // Sometimes the hardware is operating with a different channel count from the app.
+ // Then we require conversion in AAudio.
int32_t mDeviceChannelCount = 0;
};
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 0719fe1..4a0e6da 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -20,6 +20,7 @@
#include <utils/Log.h>
#include <algorithm>
+#include <audio_utils/primitives.h>
#include <aaudio/AAudio.h>
#include "client/AudioStreamInternalCapture.h"
@@ -165,35 +166,36 @@
// Read data in one or two parts.
for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
int32_t framesToProcess = framesLeft;
- int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
+ const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
if (framesAvailable <= 0) break;
if (framesToProcess > framesAvailable) {
framesToProcess = framesAvailable;
}
- int32_t numBytes = getBytesPerFrame() * framesToProcess;
- int32_t numSamples = framesToProcess * getSamplesPerFrame();
+ const int32_t numBytes = getBytesPerFrame() * framesToProcess;
+ const int32_t numSamples = framesToProcess * getSamplesPerFrame();
+ const audio_format_t sourceFormat = getDeviceFormat();
+ const audio_format_t destinationFormat = getFormat();
// TODO factor this out into a utility function
- if (getDeviceFormat() == getFormat()) {
+ if (sourceFormat == destinationFormat) {
memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
- } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
- && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) wrappingBuffer.data[partIndex],
+ } else if (sourceFormat == AUDIO_FORMAT_PCM_16_BIT
+ && destinationFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_to_float_from_i16(
(float *) destination,
- numSamples,
- 1.0f);
- } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT
- && getFormat() == AAUDIO_FORMAT_PCM_I16) {
- AAudioConvert_floatToPcm16(
- (const float *) wrappingBuffer.data[partIndex],
+ (const int16_t *) wrappingBuffer.data[partIndex],
+ numSamples);
+ } else if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT
+ && destinationFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ memcpy_to_i16_from_float(
(int16_t *) destination,
- numSamples,
- 1.0f);
+ (const float *) wrappingBuffer.data[partIndex],
+ numSamples);
} else {
- ALOGE("Format conversion not supported!");
+ ALOGE("%s() - Format conversion not supported! audio_format_t source = %u, dest = %u",
+ __func__, sourceFormat, destinationFormat);
return AAUDIO_ERROR_INVALID_FORMAT;
}
destination += numBytes;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 795ba2c..2ae37a5 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -43,9 +43,17 @@
aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AudioStreamInternal::open(builder);
if (result == AAUDIO_OK) {
+ result = mFlowGraph.configure(getFormat(),
+ getSamplesPerFrame(),
+ getDeviceFormat(),
+ getDeviceChannelCount());
+
+ if (result != AAUDIO_OK) {
+ close();
+ }
// Sample rate is constrained to common values by now and should not overflow.
int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
- mVolumeRamp.setLengthInFrames(numFrames);
+ mFlowGraph.setRampLengthInFrames(numFrames);
}
return result;
}
@@ -216,22 +224,10 @@
}
int32_t numBytes = getBytesPerFrame() * framesToWrite;
- // Data conversion.
- float levelFrom;
- float levelTo;
- mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
- AAudioDataConverter::FormattedData source(
- (void *)byteBuffer,
- getFormat(),
- getSamplesPerFrame());
- AAudioDataConverter::FormattedData destination(
- wrappingBuffer.data[partIndex],
- getDeviceFormat(),
- getDeviceChannelCount());
-
- AAudioDataConverter::convert(source, destination, framesToWrite,
- levelFrom, levelTo);
+ mFlowGraph.process((void *)byteBuffer,
+ wrappingBuffer.data[partIndex],
+ framesToWrite);
byteBuffer += numBytes;
framesLeft -= framesToWrite;
@@ -313,6 +309,6 @@
float combinedVolume = mStreamVolume * getDuckAndMuteVolume();
ALOGD("%s() mStreamVolume * duckAndMuteVolume = %f * %f = %f",
__func__, mStreamVolume, getDuckAndMuteVolume(), combinedVolume);
- mVolumeRamp.setTarget(combinedVolume);
+ mFlowGraph.setTargetVolume(combinedVolume);
return android::NO_ERROR;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 977a909..cab2942 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -21,6 +21,7 @@
#include <aaudio/AAudio.h>
#include "binding/AAudioServiceInterface.h"
+#include "client/AAudioFlowGraph.h"
#include "client/AudioStreamInternal.h"
using android::sp;
@@ -93,7 +94,7 @@
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
- LinearRamp mVolumeRamp;
+ AAudioFlowGraph mFlowGraph;
};