1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.audio; 17 18 import com.google.android.exoplayer2.C; 19 import com.google.android.exoplayer2.Format; 20 import java.nio.ByteBuffer; 21 22 /** 23 * An {@link AudioProcessor} that converts different PCM audio encodings to 16-bit integer PCM. The 24 * following encodings are supported as input: 25 * 26 * <ul> 27 * <li>{@link C#ENCODING_PCM_8BIT} 28 * <li>{@link C#ENCODING_PCM_16BIT} ({@link #isActive()} will return {@code false}) 29 * <li>{@link C#ENCODING_PCM_16BIT_BIG_ENDIAN} 30 * <li>{@link C#ENCODING_PCM_24BIT} 31 * <li>{@link C#ENCODING_PCM_32BIT} 32 * <li>{@link C#ENCODING_PCM_FLOAT} 33 * </ul> 34 */ 35 /* package */ final class ResamplingAudioProcessor extends BaseAudioProcessor { 36 37 @Override onConfigure(AudioFormat inputAudioFormat)38 public AudioFormat onConfigure(AudioFormat inputAudioFormat) 39 throws UnhandledAudioFormatException { 40 @C.PcmEncoding int encoding = inputAudioFormat.encoding; 41 if (encoding != C.ENCODING_PCM_8BIT 42 && encoding != C.ENCODING_PCM_16BIT 43 && encoding != C.ENCODING_PCM_16BIT_BIG_ENDIAN 44 && encoding != C.ENCODING_PCM_24BIT 45 && encoding != C.ENCODING_PCM_32BIT 46 && encoding != C.ENCODING_PCM_FLOAT) { 47 throw new UnhandledAudioFormatException(inputAudioFormat); 48 } 49 return encoding != C.ENCODING_PCM_16BIT 50 ? new AudioFormat( 51 inputAudioFormat.sampleRate, inputAudioFormat.channelCount, C.ENCODING_PCM_16BIT) 52 : AudioFormat.NOT_SET; 53 } 54 55 @Override queueInput(ByteBuffer inputBuffer)56 public void queueInput(ByteBuffer inputBuffer) { 57 // Prepare the output buffer. 58 int position = inputBuffer.position(); 59 int limit = inputBuffer.limit(); 60 int size = limit - position; 61 int resampledSize; 62 switch (inputAudioFormat.encoding) { 63 case C.ENCODING_PCM_8BIT: 64 resampledSize = size * 2; 65 break; 66 case C.ENCODING_PCM_16BIT_BIG_ENDIAN: 67 resampledSize = size; 68 break; 69 case C.ENCODING_PCM_24BIT: 70 resampledSize = (size / 3) * 2; 71 break; 72 case C.ENCODING_PCM_32BIT: 73 case C.ENCODING_PCM_FLOAT: 74 resampledSize = size / 2; 75 break; 76 case C.ENCODING_PCM_16BIT: 77 case C.ENCODING_INVALID: 78 case Format.NO_VALUE: 79 default: 80 throw new IllegalStateException(); 81 } 82 83 // Resample the little endian input and update the input/output buffers. 84 ByteBuffer buffer = replaceOutputBuffer(resampledSize); 85 switch (inputAudioFormat.encoding) { 86 case C.ENCODING_PCM_8BIT: 87 // 8 -> 16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up. 88 for (int i = position; i < limit; i++) { 89 buffer.put((byte) 0); 90 buffer.put((byte) ((inputBuffer.get(i) & 0xFF) - 128)); 91 } 92 break; 93 case C.ENCODING_PCM_16BIT_BIG_ENDIAN: 94 // Big endian to little endian resampling. Swap the byte order. 95 for (int i = position; i < limit; i += 2) { 96 buffer.put(inputBuffer.get(i + 1)); 97 buffer.put(inputBuffer.get(i)); 98 } 99 break; 100 case C.ENCODING_PCM_24BIT: 101 // 24 -> 16 bit resampling. Drop the least significant byte. 102 for (int i = position; i < limit; i += 3) { 103 buffer.put(inputBuffer.get(i + 1)); 104 buffer.put(inputBuffer.get(i + 2)); 105 } 106 break; 107 case C.ENCODING_PCM_32BIT: 108 // 32 -> 16 bit resampling. Drop the two least significant bytes. 109 for (int i = position; i < limit; i += 4) { 110 buffer.put(inputBuffer.get(i + 2)); 111 buffer.put(inputBuffer.get(i + 3)); 112 } 113 break; 114 case C.ENCODING_PCM_FLOAT: 115 // 32 bit floating point -> 16 bit resampling. Floating point values are in the range 116 // [-1.0, 1.0], so need to be scaled by Short.MAX_VALUE. 117 for (int i = position; i < limit; i += 4) { 118 short value = (short) (inputBuffer.getFloat(i) * Short.MAX_VALUE); 119 buffer.put((byte) (value & 0xFF)); 120 buffer.put((byte) ((value >> 8) & 0xFF)); 121 } 122 break; 123 case C.ENCODING_PCM_16BIT: 124 case C.ENCODING_INVALID: 125 case Format.NO_VALUE: 126 default: 127 // Never happens. 128 throw new IllegalStateException(); 129 } 130 inputBuffer.position(inputBuffer.limit()); 131 buffer.flip(); 132 } 133 134 } 135