Skip to content

Commit

Permalink
Fix AAC decoder initialisation (#118)
Browse files Browse the repository at this point in the history
* Fix AAC decoder initialisation for SBR/PS profiles, fix initialisation for track decoder configs

* fall through

* Remove note -- no longer the case

* Update PcmChunkDecoder.java
  • Loading branch information
devoxin authored May 22, 2024
1 parent 83f3633 commit b46a122
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ public class PcmChunkDecoder implements AudioChunkDecoder {
*/
public PcmChunkDecoder(AudioDataFormat format, boolean bigEndian) {
this.encodedAsByte = ByteBuffer.allocate(format.maximumChunkSize());

if (!bigEndian) {
encodedAsByte.order(ByteOrder.LITTLE_ENDIAN);
}

encodedAsByte.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
this.encodedAsShort = encodedAsByte.asShortBuffer();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ public class PcmChunkEncoder implements AudioChunkEncoder {
*/
public PcmChunkEncoder(AudioDataFormat format, boolean bigEndian) {
this.encoded = ByteBuffer.allocate(format.maximumChunkSize());

if (!bigEndian) {
encoded.order(ByteOrder.LITTLE_ENDIAN);
}

encoded.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
this.encodedAsShort = encoded.asShortBuffer();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.sedmelluq.discord.lavaplayer.natives.aac;

import com.sedmelluq.discord.lavaplayer.tools.io.BitStreamReader;
import com.sedmelluq.discord.lavaplayer.tools.io.BitStreamWriter;
import com.sedmelluq.discord.lavaplayer.tools.io.ByteBufferOutputStream;
import com.sedmelluq.lava.common.natives.NativeResourceHolder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
Expand All @@ -14,15 +16,21 @@
* layer. The only AAC type verified to work with this is AAC_LC.
*/
public class AacDecoder extends NativeResourceHolder {
private static final int[] SAMPLERATE_TABLE = { 96000, 88200, 64000, 48000, 44100,
32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 };

private static final int TRANSPORT_NONE = 0;

// profiles
public static final int AAC_LC = 2;
public static final int SBR = 5; // HE-AAC
public static final int PS = 29; // HE-AACv2

private static final ShortBuffer NO_BUFFER = ByteBuffer.allocateDirect(0).asShortBuffer();

private static final int ERROR_NOT_ENOUGH_BITS = 4098;
private static final int ERROR_OUTPUT_BUFFER_TOO_SMALL = 8204;

public static final int AAC_LC = 2;

private final AacDecoderLibrary library;
private final long instance;

Expand All @@ -43,8 +51,10 @@ public AacDecoder() {
* @throws IllegalStateException If the decoder has already been closed.
*/
public int configure(int objectType, int frequency, int channels) {
long buffer = encodeConfiguration(objectType, frequency, channels);
int extensionFrequency = isSbrOrPs(objectType) ? frequency * 2 : frequency;
int extensionProfile = isSbrOrPs(objectType) ? AAC_LC : objectType;

long buffer = encodeConfiguration(objectType, frequency, channels, extensionFrequency, extensionProfile);
return configureRaw(buffer);
}

Expand All @@ -61,10 +71,28 @@ public int configure(byte[] config) {

long buffer = 0;
for (int i = 0; i < config.length; i++) {
buffer |= ((long) config[i]) << (i << 3);
// & 0xff converts to unsigned long. Thanks to Viztea for finding this
buffer |= ((long) (config[i] & 0xff)) << (i << 3);
}

return configureRaw(buffer);
int ret = configureRaw(buffer);

if (ret != 0) {
try (ByteArrayInputStream stream = new ByteArrayInputStream(config)) {
BitStreamReader reader = new BitStreamReader(stream);

int objectType = reader.asInteger(5);
int sampleRateIndex = reader.asInteger(4);
int sampleRate = sampleRateIndex == 15 ? reader.asInteger(24) : SAMPLERATE_TABLE[sampleRateIndex];
int channels = reader.asInteger(4);

return configure(objectType, sampleRate, channels);
} catch (IOException ignored) {

}
}

return ret;
}

private synchronized int configureRaw(long buffer) {
Expand All @@ -77,7 +105,15 @@ private synchronized int configureRaw(long buffer) {
return library.configure(instance, buffer);
}

private static long encodeConfiguration(int objectType, int frequency, int channels) {
private static boolean isSbrOrPs(int objectType) {
return objectType == SBR || objectType == PS;
}

private static long encodeConfiguration(int objectType,
int frequency,
int channels,
int extensionFrequency,
int extensionProfile) {
try {
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(ByteOrder.nativeOrder());
Expand All @@ -93,8 +129,19 @@ private static long encodeConfiguration(int objectType, int frequency, int chann
}

bitWriter.write(channels, 4);
bitWriter.flush();

if (isSbrOrPs(objectType)) {
int extensionFrequencyIndex = getFrequencyIndex(extensionFrequency);
bitWriter.write(extensionFrequencyIndex, 4);

if (extensionFrequencyIndex == 15) {
bitWriter.write(extensionFrequency, 24);
}

bitWriter.write(extensionProfile, 5);
}

bitWriter.flush();
buffer.clear();

return buffer.getLong();
Expand All @@ -104,36 +151,13 @@ private static long encodeConfiguration(int objectType, int frequency, int chann
}

private static int getFrequencyIndex(int frequency) {
switch (frequency) {
case 96000:
return 0;
case 88200:
return 1;
case 64000:
return 2;
case 48000:
return 3;
case 44100:
return 4;
case 32000:
return 5;
case 24000:
return 6;
case 22050:
return 7;
case 16000:
return 8;
case 12000:
return 9;
case 11025:
return 10;
case 8000:
return 11;
case 7350:
return 12;
default:
return 15;
for (int i = 0; i < SAMPLERATE_TABLE.length; i++) {
if (SAMPLERATE_TABLE[i] == frequency) {
return i;
}
}

return 15;
}

/**
Expand Down

0 comments on commit b46a122

Please sign in to comment.