Skip to content Skip to sidebar Skip to footer

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"