Setting An App To Output All Audio On A Single Channel
I want to be able to play all audio from one app on only one channel on a device e.g only on left speakers. I can do this with MediaPlayer playing an mp3 file like this to play onl
Solution 1:
Using Exoplayer I can achieve this by creating a custom AudioProcessor adopted from the ChannelMappingAudioProcessor
publicclassStereoVolumeProcessorimplementsAudioProcessor {
privateint channelCount;
privateint sampleRateHz;
privateint[] pendingOutputChannels;
privateboolean active;
privateint[] outputChannels;
private ByteBuffer buffer;
private ByteBuffer outputBuffer;
privateboolean inputEnded;
privatefloat[] volume;
privatestaticfinalintLEFT_SPEAKER=0;
privatestaticfinalintRIGHT_SPEAKER=1;
publicStereoVolumeProcessor() {
buffer = EMPTY_BUFFER;
outputBuffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
}
publicvoidsetChannelMap(int[] outputChannels) {
pendingOutputChannels = outputChannels;
}
@Overridepublicbooleanconfigure(int sampleRateHz, int channelCount, @C.Encoding int encoding)throws UnhandledFormatException {
if(volume == null){
thrownewIllegalStateException("volume has not been set! Call setVolume(float left,float right)");
}
booleanoutputChannelsChanged= !Arrays.equals(pendingOutputChannels, outputChannels);
outputChannels = pendingOutputChannels;
if (outputChannels == null) {
active = false;
return outputChannelsChanged;
}
if (encoding != C.ENCODING_PCM_16BIT) {
thrownewUnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (!outputChannelsChanged && this.sampleRateHz == sampleRateHz
&& this.channelCount == channelCount) {
returnfalse;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
active = true;
returntrue;
}
@OverridepublicbooleanisActive() {
return active;
}
@OverridepublicintgetOutputChannelCount() {
returnoutputChannels== null ? channelCount : outputChannels.length;
}
@OverridepublicintgetOutputEncoding() {
return C.ENCODING_PCM_16BIT;
}
/**
* Returns the sample rate of audio output by the processor, in hertz. The value may change as a
* result of calling {@link #configure(int, int, int)} and is undefined if the instance is not
* active.
*/@OverridepublicintgetOutputSampleRateHz() {
return sampleRateHz;
}
@OverridepublicvoidqueueInput(ByteBuffer inputBuffer) {
intposition= inputBuffer.position();
intlimit= inputBuffer.limit();
intsize= limit - position;
if (buffer.capacity() < size) {
buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
} else {
buffer.clear();
}
if(isActive()){
intch=0;
for(inti= position;i<limit;i+=2){
shortsample= (short) (inputBuffer.getShort(i)* volume[ch++]);
buffer.putShort(sample);
ch%=channelCount;
}
}else{
thrownewIllegalStateException();
}
inputBuffer.position(limit);
buffer.flip();
outputBuffer = buffer;
}
@OverridepublicvoidqueueEndOfStream() {
inputEnded = true;
}
/**
* Sets the volume of right and left channels/speakers
* The values are between 0.0 and 1.0
*
* @param left
* @param right
*/publicvoidsetVolume(float left,float right){
volume = newfloat[]{left,right};
}
publicfloatgetLeftVolume(){
return volume[LEFT_SPEAKER];
}
publicfloatgetRightVolume(){
return volume[RIGHT_SPEAKER];
}
@Overridepublic ByteBuffer getOutput() {
ByteBufferoutputBuffer=this.outputBuffer;
this.outputBuffer = EMPTY_BUFFER;
return outputBuffer;
}
@SuppressWarnings("ReferenceEquality")@OverridepublicbooleanisEnded() {
return inputEnded && outputBuffer == EMPTY_BUFFER;
}
@Overridepublicvoidflush() {
outputBuffer = EMPTY_BUFFER;
inputEnded = false;
}
@Overridepublicvoidreset() {
flush();
buffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
outputChannels = null;
active = false;
}
}
Volume can be set with a value between 0.0 and 1.0 on either left, right or both speakers.
Use the processor as follows
stereoVolumeProcessor = new StereoVolumeProcessor();
stereoVolumeProcessor.setChannelMap(newint[]{0,1});
stereoVolumeProcessor.setVolume(1,0);
RenderersFactory factory = new DefaultRenderersFactory(this){
/**
* Builds an array of {@link AudioProcessor}s that will process PCM audio before output.
*/
@Override
protected AudioProcessor[] buildAudioProcessors() {
returnnew AudioProcessor[] {stereoVolumeProcessor};
}
};
Post a Comment for "Setting An App To Output All Audio On A Single Channel"