public final class

ChannelMixingMatrix

extends java.lang.Object

 java.lang.Object

↳androidx.media3.common.audio.ChannelMixingMatrix

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-common', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-common
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-common:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

An immutable matrix that describes the mapping of input channels to output channels.

The matrix coefficients define the scaling factor to use when mixing samples from the input channel (row) to the output channel (column).

Examples:

  • Stereo to mono with each channel at half volume:
             [0.5 0.5]
  • Stereo to stereo with no mixing or scaling:
             [1 0
              0 1]
  • Stereo to stereo with 0.7 volume:
             [0.7 0
              0 0.7]

Summary

Constructors
publicChannelMixingMatrix(int inputChannelCount, int outputChannelCount, float[] coefficients[])

Creates a matrix with the given coefficients in row-major order.

Methods
public static ChannelMixingMatrixcreate(int inputChannelCount, int outputChannelCount)

Creates a standard channel mixing matrix that converts from inputChannelCount channels to outputChannelCount channels.

public intgetInputChannelCount()

public floatgetMixingCoefficient(int inputChannel, int outputChannel)

Gets the scaling factor for the given input and output channel.

public intgetOutputChannelCount()

public booleanisDiagonal()

Returns whether the matrix is square and all non-diagonal coefficients are zero.

public booleanisIdentity()

Returns whether this is an identity matrix.

public booleanisSquare()

Returns whether the input and output channel count is the same.

public booleanisZero()

Returns whether all mixing coefficients are zero.

public ChannelMixingMatrixscaleBy(float scale)

Returns a new matrix with the given scaling factor applied to all coefficients.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public ChannelMixingMatrix(int inputChannelCount, int outputChannelCount, float[] coefficients[])

Creates a matrix with the given coefficients in row-major order.

Parameters:

inputChannelCount: Number of input channels (rows in the matrix).
outputChannelCount: Number of output channels (columns in the matrix).
coefficients: Non-negative matrix coefficients in row-major order.

Methods

public static ChannelMixingMatrix create(int inputChannelCount, int outputChannelCount)

Creates a standard channel mixing matrix that converts from inputChannelCount channels to outputChannelCount channels.

If the input and output channel counts match then a simple identity matrix will be returned. Otherwise, default matrix coefficients will be used to best match channel locations and overall power level.

Parameters:

inputChannelCount: Number of input channels.
outputChannelCount: Number of output channels.

Returns:

New channel mixing matrix.

public int getInputChannelCount()

public int getOutputChannelCount()

public float getMixingCoefficient(int inputChannel, int outputChannel)

Gets the scaling factor for the given input and output channel.

public boolean isZero()

Returns whether all mixing coefficients are zero.

public boolean isSquare()

Returns whether the input and output channel count is the same.

public boolean isDiagonal()

Returns whether the matrix is square and all non-diagonal coefficients are zero.

public boolean isIdentity()

Returns whether this is an identity matrix.

public ChannelMixingMatrix scaleBy(float scale)

Returns a new matrix with the given scaling factor applied to all coefficients.

Source

/*
 * Copyright 2022 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.
 */
package androidx.media3.common.audio;

import static androidx.media3.common.util.Assertions.checkArgument;

import androidx.media3.common.util.UnstableApi;

/**
 * An immutable matrix that describes the mapping of input channels to output channels.
 *
 * <p>The matrix coefficients define the scaling factor to use when mixing samples from the input
 * channel (row) to the output channel (column).
 *
 * <p>Examples:
 *
 * <ul>
 *   <li>Stereo to mono with each channel at half volume:
 *       <pre>
 *         [0.5 0.5]</pre>
 *   <li>Stereo to stereo with no mixing or scaling:
 *       <pre>
 *         [1 0
 *          0 1]</pre>
 *   <li>Stereo to stereo with 0.7 volume:
 *       <pre>
 *         [0.7 0
 *          0 0.7]</pre>
 * </ul>
 */
@UnstableApi
public final class ChannelMixingMatrix {
  private final int inputChannelCount;
  private final int outputChannelCount;
  private final float[] coefficients;
  private final boolean isZero;
  private final boolean isDiagonal;
  private final boolean isIdentity;

  /**
   * Creates a standard channel mixing matrix that converts from {@code inputChannelCount} channels
   * to {@code outputChannelCount} channels.
   *
   * <p>If the input and output channel counts match then a simple identity matrix will be returned.
   * Otherwise, default matrix coefficients will be used to best match channel locations and overall
   * power level.
   *
   * @param inputChannelCount Number of input channels.
   * @param outputChannelCount Number of output channels.
   * @return New channel mixing matrix.
   * @throws UnsupportedOperationException If no default matrix coefficients are implemented for the
   *     given input and output channel counts.
   */
  public static ChannelMixingMatrix create(int inputChannelCount, int outputChannelCount) {
    return new ChannelMixingMatrix(
        inputChannelCount,
        outputChannelCount,
        createMixingCoefficients(inputChannelCount, outputChannelCount));
  }

  /**
   * Creates a matrix with the given coefficients in row-major order.
   *
   * @param inputChannelCount Number of input channels (rows in the matrix).
   * @param outputChannelCount Number of output channels (columns in the matrix).
   * @param coefficients Non-negative matrix coefficients in row-major order.
   */
  public ChannelMixingMatrix(int inputChannelCount, int outputChannelCount, float[] coefficients) {
    checkArgument(inputChannelCount > 0, "Input channel count must be positive.");
    checkArgument(outputChannelCount > 0, "Output channel count must be positive.");
    checkArgument(
        coefficients.length == inputChannelCount * outputChannelCount,
        "Coefficient array length is invalid.");
    this.inputChannelCount = inputChannelCount;
    this.outputChannelCount = outputChannelCount;
    this.coefficients = checkCoefficientsValid(coefficients);

    // Calculate matrix properties.
    boolean allDiagonalCoefficientsAreOne = true;
    boolean allCoefficientsAreZero = true;
    boolean allNonDiagonalCoefficientsAreZero = true;
    for (int row = 0; row < inputChannelCount; row++) {
      for (int col = 0; col < outputChannelCount; col++) {
        float coefficient = getMixingCoefficient(row, col);
        boolean onDiagonal = row == col;

        if (coefficient != 1f && onDiagonal) {
          allDiagonalCoefficientsAreOne = false;
        }
        if (coefficient != 0f) {
          allCoefficientsAreZero = false;
          if (!onDiagonal) {
            allNonDiagonalCoefficientsAreZero = false;
          }
        }
      }
    }
    isZero = allCoefficientsAreZero;
    isDiagonal = isSquare() && allNonDiagonalCoefficientsAreZero;
    isIdentity = isDiagonal && allDiagonalCoefficientsAreOne;
  }

  public int getInputChannelCount() {
    return inputChannelCount;
  }

  public int getOutputChannelCount() {
    return outputChannelCount;
  }

  /** Gets the scaling factor for the given input and output channel. */
  public float getMixingCoefficient(int inputChannel, int outputChannel) {
    return coefficients[inputChannel * outputChannelCount + outputChannel];
  }

  /** Returns whether all mixing coefficients are zero. */
  public boolean isZero() {
    return isZero;
  }

  /** Returns whether the input and output channel count is the same. */
  public boolean isSquare() {
    return inputChannelCount == outputChannelCount;
  }

  /** Returns whether the matrix is square and all non-diagonal coefficients are zero. */
  public boolean isDiagonal() {
    return isDiagonal;
  }

  /** Returns whether this is an identity matrix. */
  public boolean isIdentity() {
    return isIdentity;
  }

  /** Returns a new matrix with the given scaling factor applied to all coefficients. */
  public ChannelMixingMatrix scaleBy(float scale) {
    float[] scaledCoefficients = new float[coefficients.length];
    for (int i = 0; i < coefficients.length; i++) {
      scaledCoefficients[i] = scale * coefficients[i];
    }
    return new ChannelMixingMatrix(inputChannelCount, outputChannelCount, scaledCoefficients);
  }

  private static float[] createMixingCoefficients(int inputChannelCount, int outputChannelCount) {
    if (inputChannelCount == outputChannelCount) {
      return initializeIdentityMatrix(outputChannelCount);
    }
    if (inputChannelCount == 1 && outputChannelCount == 2) {
      // Mono -> stereo.
      return new float[] {1f, 1f};
    }
    if (inputChannelCount == 2 && outputChannelCount == 1) {
      // Stereo -> mono.
      return new float[] {0.5f, 0.5f};
    }
    throw new UnsupportedOperationException(
        "Default channel mixing coefficients for "
            + inputChannelCount
            + "->"
            + outputChannelCount
            + " are not yet implemented.");
  }

  private static float[] initializeIdentityMatrix(int channelCount) {
    float[] coefficients = new float[channelCount * channelCount];
    for (int c = 0; c < channelCount; c++) {
      coefficients[channelCount * c + c] = 1f;
    }
    return coefficients;
  }

  private static float[] checkCoefficientsValid(float[] coefficients) {
    for (int i = 0; i < coefficients.length; i++) {
      if (coefficients[i] < 0f) {
        throw new IllegalArgumentException("Coefficient at index " + i + " is negative.");
      }
    }
    return coefficients;
  }
}