return num_out_samples * sizeof(*(out_buff)); \
}
+/* Channel expands from an input buffer to an output buffer.
+ * See expand_selected_channels() function below for parameter definitions.
+ * Selected channels are replaced in the output buffer, with any extra channels
+ * per frame left alone.
+ *
+ * Move from back to front so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ * NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
+ */
+/* This is written as a C macro because it operates on generic types,
+ * which in a C++ file could be alternatively achieved by a "template"
+ * or an "auto" declaration.
+ * TODO: convert this from a C file to a C++ file.
+ */
+#define EXPAND_SELECTED_CHANNELS( \
+ in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes) \
+{ \
+ size_t num_in_samples = (num_in_bytes) / sizeof(*(in_buff)); \
+ size_t num_out_samples = (num_in_samples * (out_buff_chans)) / (in_buff_chans); \
+ typeof(out_buff) dst_ptr = (out_buff) + num_out_samples - 1; \
+ size_t src_index; \
+ typeof(in_buff) src_ptr = (in_buff) + num_in_samples - 1; \
+ size_t num_extra_chans = (out_buff_chans) - (in_buff_chans); \
+ for (src_index = 0; src_index < num_in_samples; src_index += (in_buff_chans)) { \
+ dst_ptr -= num_extra_chans; \
+ for (size_t dst_offset = num_extra_chans; dst_offset < (out_buff_chans); dst_offset++) { \
+ *dst_ptr-- = *src_ptr--; \
+ } \
+ } \
+ /* return number of *bytes* generated */ \
+ return num_out_samples * sizeof(*(out_buff)); \
+}
+
+
/* Channel expands from a MONO input buffer to a MULTICHANNEL output buffer by duplicating the
* single input channel to the first 2 output channels and 0-filling the remaining.
* See expand_channels() function below for parameter definitions.
return num_in_bytes;
}
+
+/*
+ * Convert a buffer of N-channel, interleaved samples to M-channel
+ * (where N < M).
+ * in_buff points to the buffer of samples
+ * in_buff_channels Specifies the number of channels in the input buffer.
+ * out_buff points to the buffer to receive converted samples.
+ * out_buff_channels Specifies the number of channels in the output buffer.
+ * sample_size_in_bytes Specifies the number of bytes per sample.
+ * num_in_bytes size of input buffer in BYTES
+ * returns
+ * the number of BYTES of output data.
+ * NOTE
+ * channels > N are left alone in out_buff.
+ * The out and in buffers must either be completely separate (non-overlapping), or
+ * they must both start at the same address. Partially overlapping buffers are not supported.
+ */
+static size_t expand_selected_channels(const void* in_buff, size_t in_buff_chans,
+ void* out_buff, size_t out_buff_chans,
+ unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+ switch (sample_size_in_bytes) {
+ case 1:
+
+ EXPAND_SELECTED_CHANNELS((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 2:
+
+ EXPAND_SELECTED_CHANNELS((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 3:
+
+ EXPAND_SELECTED_CHANNELS((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 4:
+
+ EXPAND_SELECTED_CHANNELS((const int32_t*)in_buff, in_buff_chans,
+ (int32_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ default:
+ return 0;
+ }
+}
+
+size_t adjust_selected_channels(const void* in_buff, size_t in_buff_chans,
+ void* out_buff, size_t out_buff_chans,
+ unsigned sample_size_in_bytes, size_t num_in_bytes)
+{
+ if (out_buff_chans > in_buff_chans) {
+ return expand_selected_channels(in_buff, in_buff_chans, out_buff, out_buff_chans,
+ sample_size_in_bytes, num_in_bytes);
+ } else if (out_buff_chans < in_buff_chans) {
+ return contract_channels(in_buff, in_buff_chans, out_buff, out_buff_chans,
+ sample_size_in_bytes, num_in_bytes);
+ } else if (in_buff != out_buff) {
+ memcpy(out_buff, in_buff, num_in_bytes);
+ }
+
+ return num_in_bytes;
+}
+
void* out_buff, size_t out_buff_chans,
unsigned sample_size_in_bytes, size_t num_in_bytes);
+/**
+ * Expands or contracts sample data from one interleaved channel format to another.
+ * Extra expanded channels are left alone in the output buffer.
+ * Contracted channels are omitted from the end of each audio frame.
+ *
+ * \param in_buff points to the buffer of samples
+ * \param in_buff_chans Specifies the number of channels in the input buffer.
+ * \param out_buff points to the buffer to receive converted samples.
+ * \param out_buff_chans Specifies the number of channels in the output buffer.
+ * \param sample_size_in_bytes Specifies the number of bytes per sample. 1, 2, 3, 4 are
+ * currently valid.
+ * \param num_in_bytes size of input buffer in BYTES
+ *
+ * \return
+ * the number of BYTES of output data or 0 if an error occurs.
+ *
+ * \note
+ * The out and in buffers must either be completely separate (non-overlapping), or
+ * they must both start at the same address. Partially overlapping buffers are not supported.
+ */
+size_t adjust_selected_channels(const void* in_buff, size_t in_buff_chans,
+ void* out_buff, size_t out_buff_chans,
+ unsigned sample_size_in_bytes, size_t num_in_bytes);
+
/** \cond */
__END_DECLS
/** \endcond */
},
}
}
+
+cc_test {
+ name: "channels_tests",
+ host_supported: true,
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+ srcs: ["channels_tests.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ target: {
+ android: {
+ shared_libs: ["libaudioutils"],
+ },
+ host: {
+ static_libs: ["libaudioutils"],
+ },
+ }
+}
adb push $OUT/data/nativetest/primitives_tests/primitives_tests /system/bin
adb shell /system/bin/primitives_tests
-adb push $OUT/system/bin/primitives_benchmark /system/bin
-adb shell /system/bin/primitives_benchmark
-
echo "testing power"
adb push $OUT/data/nativetest/power_tests/power_tests /system/bin
adb shell /system/bin/power_tests
+
+echo "testing channels"
+adb push $OUT/data/nativetest/channels_tests/channels_tests /system/bin
+adb shell /system/bin/channels_tests
+
+echo "benchmarking primitives"
+adb push $OUT/system/bin/primitives_benchmark /system/bin
+adb shell /system/bin/primitives_benchmark
--- /dev/null
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "audio_utils_channels_tests"
+
+#include <math.h>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <audio_utils/channels.h>
+
+// TODO: Make a common include file for helper functions.
+
+template<typename T>
+void checkMonotone(const T *ary, size_t size)
+{
+ for (size_t i = 1; i < size; ++i) {
+ EXPECT_LT(ary[i-1], ary[i]);
+ }
+}
+
+template<typename T>
+void checkUnsignedMonotoneOrZero(const T *ary, size_t size)
+{
+ if (size == 0) return;
+
+ T least = ary[0];
+ for (size_t i = 1; i < size; ++i) {
+ if (ary[i]) {
+ EXPECT_LT(least, ary[i]);
+ least = ary[i];
+ }
+ }
+}
+
+template<typename T>
+void expectEq(const T &c1, const T &c2) {
+ EXPECT_EQ(c1.size(), c2.size());
+ EXPECT_EQ(0, memcmp(c1.data(), c2.data(), sizeof(c1[0]) * std::min(c1.size(), c2.size())));
+}
+
+TEST(audio_utils_channels, adjust_channels) {
+ constexpr size_t size = 65536;
+ std::vector<uint16_t> u16ref(size);
+ std::vector<uint16_t> u16expand(size * 2);
+ std::vector<uint16_t> u16ary(size);
+
+ // reference buffer is monotonic.
+ for (size_t i = 0; i < u16ref.size(); ++i) {
+ u16ref[i] = i;
+ }
+
+ // expand channels from stereo to quad.
+ adjust_channels(
+ u16ref.data() /*in_buff*/,
+ 2 /*in_channels*/,
+ u16expand.data() /*out_buff*/,
+ 4 /*out_channels*/,
+ sizeof(u16ref[0]) /*sample_size_in_bytes*/,
+ sizeof(u16ref[0]) * u16ref.size() /*num_in_bytes*/);
+
+ // expanded buffer must increase (or be zero).
+ checkUnsignedMonotoneOrZero(u16expand.data(), u16expand.size());
+
+ // contract channels back to stereo.
+ adjust_channels(
+ u16expand.data() /*in_buff*/,
+ 4 /*in_channels*/,
+ u16ary.data() /*out_buff*/,
+ 2 /*out_channels*/,
+ sizeof(u16expand[0]) /*sample_size_in_bytes*/,
+ sizeof(u16expand[0]) * u16expand.size() /*num_in_bytes*/);
+
+ // contracted array must be identical to original.
+ expectEq(u16ary, u16ref);
+}
+
+TEST(audio_utils_channels, adjust_selected_channels) {
+ constexpr size_t size = 65536;
+ std::vector<uint16_t> u16ref(size);
+ std::vector<uint16_t> u16contract(size / 2);
+ std::vector<uint16_t> u16ary(size);
+
+ // reference buffer is monotonic.
+ for (size_t i = 0; i < u16ref.size(); ++i) {
+ u16ref[i] = i;
+ }
+
+ // contract from quad to stereo.
+ adjust_selected_channels(
+ u16ref.data() /*in_buff*/,
+ 4 /*in_channels*/,
+ u16contract.data() /*out_buff*/,
+ 2 /*out_channels*/,
+ sizeof(u16ref[0]) /*sample_size_in_bytes*/,
+ sizeof(u16ref[0]) * u16ref.size() /*num_in_bytes*/);
+
+ // contracted buffer must increase.
+ checkMonotone(u16contract.data(), u16contract.size());
+
+ // initialize channels 3 and 4 of final comparison array.
+ for (size_t i = 0; i < u16ary.size() / 4; ++i) {
+ u16ary[i * 4 + 2] = u16ref[i * 4 + 2];
+ u16ary[i * 4 + 3] = u16ref[i * 4 + 3];
+ }
+
+ // expand stereo into channels 1 and 2 of quad comparison array.
+ adjust_selected_channels(
+ u16contract.data() /*in_buff*/,
+ 2 /*in_channels*/,
+ u16ary.data() /*out_buff*/,
+ 4 /*out_channels*/,
+ sizeof(u16contract[0]) /*sample_size_in_bytes*/,
+ sizeof(u16contract[0]) * u16contract.size() /*num_in_bytes*/);
+
+ // comparison array must be identical to original.
+ expectEq(u16ary, u16ref);
+}
+
+
EXPECT_EQ(0, memcmp(i16ary.data(), i16ref.data(), sizeof(i16ref[0]) * size));
}
-TEST(audio_utils_channels, adjust_channels) {
- uint16_t *u16ref = new uint16_t[65536];
- uint16_t *u16expand = new uint16_t[65536*2];
- uint16_t *u16ary = new uint16_t[65536];
-
- // reference buffer always increases
- for (size_t i = 0; i < 65536; ++i) {
- u16ref[i] = i;
- }
-
- // expand channels from stereo to quad.
- adjust_channels(u16ref /*in_buff*/, 2 /*in_channels*/,
- u16expand /*out_buff*/, 4 /*out_channels*/,
- sizeof(u16ref[0]) /*sample_size_in_bytes*/,
- sizeof(u16ref[0])*65536 /*num_in_bytes*/);
-
- // expanded buffer must increase (or be zero)
- checkMonotoneOrZero(u16expand, 65536*2);
-
- // contract channels back to stereo.
- adjust_channels(u16expand /*in_buff*/, 4 /*in_channels*/,
- u16ary /*out_buff*/, 2 /*out_channels*/,
- sizeof(u16expand[0]) /*sample_size_in_bytes*/,
- sizeof(u16expand[0])*65536*2 /*num_in_bytes*/);
-
- // must be identical to original.
- EXPECT_EQ(0, memcmp(u16ary, u16ref, sizeof(u16ref[0])*65536));
-
- delete[] u16ref;
- delete[] u16expand;
- delete[] u16ary;
-}
-
template<typename T, typename TComparison>
void checkAddedClamped(T *out, const T *in1, const T *in2, size_t size,
TComparison limNeg, TComparison limPos)