/* * 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. */ package org.mozilla.thirdparty.com.google.android.exoplayer2.audio; import org.mozilla.thirdparty.com.google.android.exoplayer2.C; import org.mozilla.thirdparty.com.google.android.exoplayer2.Format; import java.nio.ByteBuffer; /** * An {@link AudioProcessor} that converts different PCM audio encodings to 16-bit integer PCM. The * following encodings are supported as input: * * */ /* package */ final class ResamplingAudioProcessor extends BaseAudioProcessor { @Override public AudioFormat onConfigure(AudioFormat inputAudioFormat) throws UnhandledAudioFormatException { @C.PcmEncoding int encoding = inputAudioFormat.encoding; if (encoding != C.ENCODING_PCM_8BIT && encoding != C.ENCODING_PCM_16BIT && encoding != C.ENCODING_PCM_16BIT_BIG_ENDIAN && encoding != C.ENCODING_PCM_24BIT && encoding != C.ENCODING_PCM_32BIT && encoding != C.ENCODING_PCM_FLOAT) { throw new UnhandledAudioFormatException(inputAudioFormat); } return encoding != C.ENCODING_PCM_16BIT ? new AudioFormat( inputAudioFormat.sampleRate, inputAudioFormat.channelCount, C.ENCODING_PCM_16BIT) : AudioFormat.NOT_SET; } @Override public void queueInput(ByteBuffer inputBuffer) { // Prepare the output buffer. int position = inputBuffer.position(); int limit = inputBuffer.limit(); int size = limit - position; int resampledSize; switch (inputAudioFormat.encoding) { case C.ENCODING_PCM_8BIT: resampledSize = size * 2; break; case C.ENCODING_PCM_16BIT_BIG_ENDIAN: resampledSize = size; break; case C.ENCODING_PCM_24BIT: resampledSize = (size / 3) * 2; break; case C.ENCODING_PCM_32BIT: case C.ENCODING_PCM_FLOAT: resampledSize = size / 2; break; case C.ENCODING_PCM_16BIT: case C.ENCODING_INVALID: case Format.NO_VALUE: default: throw new IllegalStateException(); } // Resample the little endian input and update the input/output buffers. ByteBuffer buffer = replaceOutputBuffer(resampledSize); switch (inputAudioFormat.encoding) { case C.ENCODING_PCM_8BIT: // 8 -> 16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up. for (int i = position; i < limit; i++) { buffer.put((byte) 0); buffer.put((byte) ((inputBuffer.get(i) & 0xFF) - 128)); } break; case C.ENCODING_PCM_16BIT_BIG_ENDIAN: // Big endian to little endian resampling. Swap the byte order. for (int i = position; i < limit; i += 2) { buffer.put(inputBuffer.get(i + 1)); buffer.put(inputBuffer.get(i)); } break; case C.ENCODING_PCM_24BIT: // 24 -> 16 bit resampling. Drop the least significant byte. for (int i = position; i < limit; i += 3) { buffer.put(inputBuffer.get(i + 1)); buffer.put(inputBuffer.get(i + 2)); } break; case C.ENCODING_PCM_32BIT: // 32 -> 16 bit resampling. Drop the two least significant bytes. for (int i = position; i < limit; i += 4) { buffer.put(inputBuffer.get(i + 2)); buffer.put(inputBuffer.get(i + 3)); } break; case C.ENCODING_PCM_FLOAT: // 32 bit floating point -> 16 bit resampling. Floating point values are in the range // [-1.0, 1.0], so need to be scaled by Short.MAX_VALUE. for (int i = position; i < limit; i += 4) { short value = (short) (inputBuffer.getFloat(i) * Short.MAX_VALUE); buffer.put((byte) (value & 0xFF)); buffer.put((byte) ((value >> 8) & 0xFF)); } break; case C.ENCODING_PCM_16BIT: case C.ENCODING_INVALID: case Format.NO_VALUE: default: // Never happens. throw new IllegalStateException(); } inputBuffer.position(inputBuffer.limit()); buffer.flip(); } }