return out;
}
+/* 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.
+ */
+
/* Channel expands (adds zeroes to audio frame end) from an input buffer to an output buffer.
* See expand_channels() function below for parameter definitions.
*
* 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.
+ *
+ * Macro has a return statement.
*/
#define EXPAND_CHANNELS(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes, zero) \
{ \
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.
+ *
+ * Macro has a return statement.
+ */
+#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)); \
+}
+
+/* Expand number of channels from an input buffer to an output buffer.
+ * See expand_channels_non_destructive() function below for parameter definitions.
+ *
+ * Input channels are copied to the output buffer, with extra output
+ * channels interleaved from back of input buffer.
+ *
+ * So for in_chans = 2, out_chans = 4: [1|2|1|2...|3|4|3|4] => [1|2|3|4|1|2|3|4...]
+ *
+ * NOTE: in_buff must be same size as out_buff and num_in_bytes must
+ * be a multiple of in_buff_channels * in_buff_sample_size.
+ *
+ * Uses a temporary buffer so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ *
+ * Macro has a return statement.
+ */
+#define EXPAND_CHANNELS_NON_DESTRUCTIVE(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); \
+ typeof(in_buff) src_ptr = (in_buff); \
+ typeof(*out_buff) temp_buff[num_in_samples]; \
+ typeof(out_buff) temp_ptr = temp_buff; \
+ /* if in-place, copy input channels to a temp buffer */ \
+ if ((in_buff) == (out_buff)) { \
+ memcpy(temp_buff, src_ptr, (num_in_bytes)); \
+ src_ptr += num_in_samples; \
+ } else { \
+ temp_ptr = (typeof(out_buff)) src_ptr; \
+ src_ptr += num_in_samples; \
+ } \
+ /* interleave channels from end of source buffer with those from front */ \
+ size_t src_index; \
+ for (src_index = 0; src_index < num_out_samples; src_index += (out_buff_chans)) { \
+ size_t dst_offset; \
+ for (dst_offset = 0; dst_offset < (in_buff_chans); dst_offset++) { \
+ *dst_ptr++ = *temp_ptr++; \
+ } \
+ for (;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.
* 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.
+ *
+ * Macro has a return statement.
*/
#define EXPAND_MONO_TO_MULTI(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes, zero) \
{ \
* Move from front to back 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.
+ *
+ * Macro has a return statement.
*/
#define CONTRACT_CHANNELS(in_buff, in_buff_chans, out_buff, out_buff_chans, num_in_bytes) \
{ \
return num_out_samples * sizeof(*(out_buff)); \
}
+/* Contract number of channels from an input buffer to an output buffer,
+ * storing removed channels at end of buffer.
+ *
+ * See contract_channels_non_destructive() function below for parameter definitions.
+ *
+ * So for in_chans = 4, out_chans = 2: [1|2|3|4|1|2|3|4...] => [1|2|1|2...|3|4|3|4]
+ *
+ * NOTE: in_buff must be same size as out_buff and num_in_bytes must
+ * be a multiple of in_buff_channels * in_buff_sample_size.
+ *
+ * Uses a temporary buffer so that the conversion can be done in-place
+ * i.e. in_buff == out_buff
+ *
+ * Macro has a return statement.
+ */
+#define CONTRACT_CHANNELS_NON_DESTRUCTIVE(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); \
+ typeof(in_buff) src_ptr = (in_buff); \
+ size_t num_temp_samples = num_in_samples - num_out_samples; \
+ typeof(*out_buff) temp_buff[num_temp_samples]; \
+ typeof(out_buff) temp_ptr; \
+ /* if in-place, copy input channels to a temp buffer instead of out buffer */ \
+ if ((in_buff) == (out_buff)) { \
+ temp_ptr = temp_buff; \
+ } else { \
+ temp_ptr = dst_ptr + num_out_samples; \
+ } \
+ size_t src_index; \
+ for (src_index = 0; src_index < num_in_samples; src_index += (in_buff_chans)) { \
+ size_t dst_offset; \
+ for (dst_offset = 0; dst_offset < (out_buff_chans); dst_offset++) { \
+ *dst_ptr++ = *src_ptr++; \
+ } \
+ for (;dst_offset < (in_buff_chans); dst_offset++) { \
+ *temp_ptr++ = *src_ptr++; \
+ } \
+ } \
+ /* if in-place, interleave channels from the temp buffer */ \
+ if ((in_buff) == (out_buff)) { \
+ temp_ptr = temp_buff; \
+ memcpy(dst_ptr, temp_ptr, num_temp_samples * sizeof(*(in_buff))); \
+ } \
+ /* return number of *bytes* generated */ \
+ return num_out_samples * sizeof(*(out_buff)); \
+}
+
/* Channel contracts from a MULTICHANNEL input buffer to a MONO output buffer by mixing the
* first two input channels into the single output channel (and skipping the rest).
* See contract_channels() function below for parameter definitions.
* NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
* NOTE: Overload of the summed channels is avoided by averaging the two input channels.
* NOTE: Can not be used for uint8x3_t samples, see CONTRACT_TO_MONO_24() below.
+ *
+ * Macro has a return statement.
*/
#define CONTRACT_TO_MONO(in_buff, out_buff, num_in_bytes) \
{ \
* NOTE: num_in_bytes must be a multiple of in_buff_channels * in_buff_sample_size.
* NOTE: Overload of the summed channels is avoided by averaging the two input channels.
* NOTE: Can not be used for normal, scalar samples, see CONTRACT_TO_MONO() above.
+ *
+ * Macro has a return statement.
*/
#define CONTRACT_TO_MONO_24(in_buff, out_buff, 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 > M are stored at the end of the output buffer.
+ * The output and input buffers must be the same length.
+ * The output and input 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 contract_channels_non_destructive(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:
+ CONTRACT_CHANNELS_NON_DESTRUCTIVE((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ case 2:
+ CONTRACT_CHANNELS_NON_DESTRUCTIVE((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ case 3:
+ CONTRACT_CHANNELS_NON_DESTRUCTIVE((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ case 4:
+ CONTRACT_CHANNELS_NON_DESTRUCTIVE((const int32_t*)in_buff, in_buff_chans,
+ (int32_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+ default:
+ return 0;
+ }
+}
+
+/*
+ * 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.
}
}
+/*
+ * 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;
+ }
+}
+
+/*
+ * 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 interleaved with data from the end of the input buffer.
+ * The output and input buffers must be the same length.
+ * The output and input 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_channels_non_destructive(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_CHANNELS_NON_DESTRUCTIVE((const uint8_t*)in_buff, in_buff_chans,
+ (uint8_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 2:
+
+ EXPAND_CHANNELS_NON_DESTRUCTIVE((const int16_t*)in_buff, in_buff_chans,
+ (int16_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 3:
+
+ EXPAND_CHANNELS_NON_DESTRUCTIVE((const uint8x3_t*)in_buff, in_buff_chans,
+ (uint8x3_t*)out_buff, out_buff_chans,
+ num_in_bytes);
+ // returns in macro
+
+ case 4:
+
+ EXPAND_CHANNELS_NON_DESTRUCTIVE((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_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)
return num_in_bytes;
}
+
+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;
+}
+
+size_t adjust_channels_non_destructive(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_channels_non_destructive(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_non_destructive(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;
+}