replaygain: revert back to copy no-op

Turns out using isActive to indicate that the AudioProcessor is a no-op
is too unreliable due to how they are managed internally.

Instead, I really do just have to use a copy. Once again ExoPlayer
picks the most absurd possible design choices for no good reason.

Resolves #293.
This commit is contained in:
Alexander Capehart 2023-01-05 11:03:30 -07:00
parent c0f1b9423f
commit 743220d0aa
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 30 additions and 35 deletions

View file

@ -6,6 +6,7 @@
- Added ability to edit previously played or currently playing items in the queue - Added ability to edit previously played or currently playing items in the queue
#### What's Fixed #### What's Fixed
- Fixed unreliable ReplayGain adjustment application in certain situations
- Fixed crash that would occur in music folders dialog when user does not have a working - Fixed crash that would occur in music folders dialog when user does not have a working
file manager file manager

View file

@ -229,49 +229,44 @@ class ReplayGainAudioProcessor(private val context: Context) :
throw AudioProcessor.UnhandledAudioFormatException(inputAudioFormat) throw AudioProcessor.UnhandledAudioFormatException(inputAudioFormat)
} }
override fun isActive() = super.isActive() && volume != 1f
override fun queueInput(inputBuffer: ByteBuffer) { override fun queueInput(inputBuffer: ByteBuffer) {
val position = inputBuffer.position() val pos = inputBuffer.position()
val limit = inputBuffer.limit() val limit = inputBuffer.limit()
val size = limit - position val buffer = replaceOutputBuffer(limit - pos)
val buffer = replaceOutputBuffer(size)
for (i in position until limit step 2) { if (volume == 1f) {
// Ensure we clamp the values to the minimum and maximum values possible // Nothing to adjust, just copy the audio data.
// for the encoding. This prevents issues where samples amplified beyond // isActive is technically a much better way of doing a no-op like this, but since
// 1 << 16 will end up becoming truncated during the conversion to a short, // the adjustment can change during playback I'm largely forced to do this.
// resulting in popping. buffer.put(inputBuffer.slice())
var sample = inputBuffer.getLeShort(i) } else {
sample = for (i in pos until limit step 2) {
(sample * volume) // 16-bit PCM audio, deserialize a little-endian short.
.toInt() var sample =
.coerceAtLeast(Short.MIN_VALUE.toInt()) inputBuffer
.coerceAtMost(Short.MAX_VALUE.toInt()) .get(i + 1)
.toShort() .toInt()
buffer.putLeShort(sample) .shl(8)
.or(inputBuffer.get(i).toInt().and(0xFF))
.toShort()
// Ensure we clamp the values to the minimum and maximum values possible
// for the encoding. This prevents issues where samples amplified beyond
// 1 << 16 will end up becoming truncated during the conversion to a short,
// resulting in popping.
sample =
(sample * volume)
.toInt()
.coerceAtLeast(Short.MIN_VALUE.toInt())
.coerceAtMost(Short.MAX_VALUE.toInt())
.toShort()
buffer.put(sample.toByte()).put(sample.toInt().shr(8).toByte())
}
} }
inputBuffer.position(limit) inputBuffer.position(limit)
buffer.flip() buffer.flip()
} }
/**
* Always read a little-endian [Short] from the [ByteBuffer] at the given index.
* @param at The index to read the [Short] from.
*/
private fun ByteBuffer.getLeShort(at: Int) =
get(at + 1).toInt().shl(8).or(get(at).toInt().and(0xFF)).toShort()
/**
* Always write a little-endian [Short] at the end of the [ByteBuffer].
* @param short The [Short] to write.
*/
private fun ByteBuffer.putLeShort(short: Short) {
put(short.toByte())
put(short.toInt().shr(8).toByte())
}
/** /**
* The resolved ReplayGain adjustment for a file. * The resolved ReplayGain adjustment for a file.
* @param track The track adjustment (in dB), or 0 if it is not present. * @param track The track adjustment (in dB), or 0 if it is not present.

View file

@ -170,7 +170,6 @@ class Queue {
* mutated. * mutated.
*/ */
fun move(src: Int, dst: Int): ChangeResult { fun move(src: Int, dst: Int): ChangeResult {
if (shuffledMapping.isNotEmpty()) { if (shuffledMapping.isNotEmpty()) {
// Move songs only in the shuffled mapping. There is no sane analogous form of // Move songs only in the shuffled mapping. There is no sane analogous form of
// this for the ordered mapping. // this for the ordered mapping.