Skip to content

Commit 1dd235c

Browse files
committed
AudioResampler fixes for more than one channel
1 parent 9e31c27 commit 1dd235c

File tree

4 files changed

+111
-31
lines changed

4 files changed

+111
-31
lines changed

webrtc-jni/src/main/cpp/include/JNI_AudioResampler.h

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webrtc-jni/src/main/cpp/src/JNI_AudioResampler.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616

1717
#include "JNI_AudioResampler.h"
1818
#include "Exception.h"
19-
#include "JavaArrayList.h"
20-
#include "JavaEnums.h"
21-
#include "JavaError.h"
2219
#include "JavaObject.h"
2320
#include "JavaRef.h"
24-
#include "JavaString.h"
2521
#include "JavaUtils.h"
2622

27-
#include "common_audio/resampler/push_sinc_resampler.h"
23+
#include "common_audio/resampler/include/push_resampler.h"
2824

2925
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioResampler_dispose
3026
(JNIEnv * env, jobject caller)
3127
{
32-
webrtc::PushSincResampler * resampler = GetHandle<webrtc::PushSincResampler>(env, caller);
28+
webrtc::PushResampler<int16_t> * resampler = GetHandle<webrtc::PushResampler<int16_t>>(env, caller);
3329
CHECK_HANDLE(resampler);
3430

3531
delete resampler;
@@ -38,17 +34,26 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioResampler_dispose
3834
}
3935

4036
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioResampler_initialize
41-
(JNIEnv * env, jobject caller, jint sourceFrames, jint targetFrames)
37+
(JNIEnv * env, jobject caller)
4238
{
43-
webrtc::PushSincResampler * resampler = new webrtc::PushSincResampler(sourceFrames, targetFrames);
39+
webrtc::PushResampler<int16_t> * resampler = new webrtc::PushResampler<int16_t>();
4440

4541
SetHandle(env, caller, resampler);
4642
}
4743

44+
JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_AudioResampler_resetInternal
45+
(JNIEnv* env, jobject caller, jint sourceRate, jint targetRate, jint channels)
46+
{
47+
webrtc::PushResampler<int16_t> * resampler = GetHandle<webrtc::PushResampler<int16_t>>(env, caller);
48+
CHECK_HANDLE(resampler);
49+
50+
resampler->InitializeIfNeeded(sourceRate, targetRate, channels);
51+
}
52+
4853
JNIEXPORT jint JNICALL Java_dev_onvoid_webrtc_media_audio_AudioResampler_resampleInternal
4954
(JNIEnv * env, jobject caller, jbyteArray samplesIn, jint nSamplesIn, jbyteArray samplesOut, jint maxSamplesOut)
5055
{
51-
webrtc::PushSincResampler * resampler = GetHandle<webrtc::PushSincResampler>(env, caller);
56+
webrtc::PushResampler<int16_t> * resampler = GetHandle<webrtc::PushResampler<int16_t>>(env, caller);
5257
CHECK_HANDLEV(resampler, -1);
5358

5459
jboolean isDstCopy = JNI_FALSE;

webrtc/src/main/java/dev/onvoid/webrtc/media/audio/AudioResampler.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,55 @@
2222

2323
/**
2424
* Audio sampling rate converter. This resampler operates on audio frames of 10
25-
* milliseconds.
25+
* milliseconds. Each sample is assumed to be a 16-bit PCM sample.
2626
*
2727
* @author Alex Andres
2828
*/
2929
public class AudioResampler extends DisposableNativeObject {
3030

31-
private final int sourceFrames;
31+
private int targetFrames;
3232

33-
private final int targetFrames;
33+
private boolean initialized;
3434

3535

36+
/**
37+
* Creates a new {@code AudioResampler} without sampling parameters. Make
38+
* sure to call {@link #reset(int, int, int)} afterwards.
39+
*/
40+
public AudioResampler() {
41+
initialize();
42+
}
43+
3644
/**
3745
* Creates a new {@code AudioResampler} with specified sampling frequency
3846
* parameters.
3947
*
4048
* @param sourceSampleRate The sampling frequency of the input signal.
4149
* @param targetSampleRate The sampling frequency of the output signal.
50+
* @param channels The number of audio channels to resample.
51+
*/
52+
public AudioResampler(int sourceSampleRate, int targetSampleRate, int channels) {
53+
initialize();
54+
reset(sourceSampleRate, targetSampleRate, channels);
55+
}
56+
57+
/**
58+
* Must be called when created with default constructor or whenever the
59+
* sampling parameters change. Can be called at any time as it is a no-op if
60+
* parameters have not changed since the last call.
61+
*
62+
* @param sourceSampleRate The sampling frequency of the input signal.
63+
* @param targetSampleRate The sampling frequency of the output signal.
64+
* @param channels The number of audio channels to resample.
4265
*/
43-
public AudioResampler(int sourceSampleRate, int targetSampleRate) {
44-
// 10 ms frames
45-
sourceFrames = sourceSampleRate / 100;
46-
targetFrames = targetSampleRate / 100;
66+
public void reset(int sourceSampleRate, int targetSampleRate, int channels) {
67+
initialized = false;
68+
69+
resetInternal(sourceSampleRate, targetSampleRate, channels);
4770

48-
initialize(sourceFrames, targetFrames);
71+
targetFrames = targetSampleRate / 100 * channels; // 10 ms frame
72+
73+
initialized = true;
4974
}
5075

5176
/**
@@ -65,7 +90,11 @@ public int resample(byte[] samplesIn, int nSamplesIn, byte[] samplesOut) {
6590
requireNonNull(samplesIn);
6691
requireNonNull(samplesOut);
6792

68-
final int arraySamplesIn = samplesIn.length / 2;
93+
if (!initialized) {
94+
throw new IllegalStateException("Not initialized: Use reset() to set parameters");
95+
}
96+
97+
final int arraySamplesIn = samplesIn.length / 2; // 16-bit PCM sample
6998
final int maxSamplesOut = samplesOut.length / 2;
7099

71100
nSamplesIn = Math.min(arraySamplesIn, Math.max(0, nSamplesIn));
@@ -80,7 +109,10 @@ public int resample(byte[] samplesIn, int nSamplesIn, byte[] samplesOut) {
80109
@Override
81110
public native void dispose();
82111

83-
private native void initialize(int sourceFrames, int targetFrames);
112+
private native void initialize();
113+
114+
private native void resetInternal(int sourceSampleRate, int targetSampleRate,
115+
int channels);
84116

85117
private native int resampleInternal(byte[] samplesIn, int nSamplesIn,
86118
byte[] samplesOut, int maxSamplesOut);

webrtc/src/test/java/dev/onvoid/webrtc/media/audio/AudioResamplerTest.java

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,63 @@
2222
import dev.onvoid.webrtc.TestBase;
2323

2424
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.BeforeEach;
2526
import org.junit.jupiter.api.Test;
2627

2728
class AudioResamplerTest extends TestBase {
2829

2930
private AudioResampler resampler;
3031

3132

33+
@BeforeEach
34+
void init() {
35+
resampler = new AudioResampler();
36+
}
37+
3238
@AfterEach
3339
void dispose() {
3440
resampler.dispose();
3541
}
3642

3743
@Test
38-
void targetBufferUnderflow() {
39-
resampler = new AudioResampler(48000, 24000);
44+
void notInitialized() {
45+
SampleBuffer buffer = new SampleBuffer(48000, 24000, 1);
46+
47+
assertThrows(IllegalStateException.class, () -> {
48+
resampler.resample(buffer.src, buffer.nSamplesIn, buffer.dst);
49+
});
50+
}
4051

41-
SampleBuffer buffer = new SampleBuffer(48000, 24000);
52+
@Test
53+
void targetBufferUnderflow() {
54+
SampleBuffer buffer = new SampleBuffer(48000, 24000, 1);
4255
buffer.setTargetBufferSize(buffer.frameSizeOut / 2);
4356

57+
reset(resampler, buffer);
58+
4459
assertThrows(IllegalArgumentException.class, () -> {
4560
resample(resampler, buffer);
4661
});
4762
}
4863

64+
@Test
65+
void constructorParameters() {
66+
SampleBuffer buffer = new SampleBuffer(48000, 44100, 2);
67+
68+
AudioResampler resampler = new AudioResampler(48000, 44100, 2);
69+
70+
int result = resample(resampler, buffer);
71+
72+
resampler.dispose();
73+
74+
assertEquals(buffer.nSamplesOut, result);
75+
}
76+
4977
@Test
5078
void downSample() {
51-
resampler = new AudioResampler(48000, 44100);
79+
SampleBuffer buffer = new SampleBuffer(48000, 44100, 1);
5280

53-
SampleBuffer buffer = new SampleBuffer(48000, 44100);
81+
reset(resampler, buffer);
5482

5583
int result = resample(resampler, buffer);
5684

@@ -59,15 +87,19 @@ void downSample() {
5987

6088
@Test
6189
void upSample() {
62-
resampler = new AudioResampler(32000, 48000);
90+
SampleBuffer buffer = new SampleBuffer(32000, 48000, 1);
6391

64-
SampleBuffer buffer = new SampleBuffer(32000, 48000);
92+
reset(resampler, buffer);
6593

6694
int result = resample(resampler, buffer);
6795

6896
assertEquals(buffer.nSamplesOut, result);
6997
}
7098

99+
private static void reset(AudioResampler resampler, SampleBuffer buffer) {
100+
resampler.reset(buffer.sampleRateIn, buffer.sampleRateOut, 1);
101+
}
102+
71103
private static int resample(AudioResampler resampler, SampleBuffer buffer) {
72104
return resampler.resample(buffer.src, buffer.nSamplesIn, buffer.dst);
73105
}
@@ -78,6 +110,8 @@ private static class SampleBuffer {
78110

79111
final int bytesPerFrame = 2;
80112

113+
final int channels;
114+
81115
final int sampleRateIn;
82116
final int sampleRateOut;
83117

@@ -91,12 +125,13 @@ private static class SampleBuffer {
91125
byte[] dst;
92126

93127

94-
SampleBuffer(int sampleRateIn, int sampleRateOut) {
128+
SampleBuffer(int sampleRateIn, int sampleRateOut, int channels) {
129+
this.channels = channels;
95130
this.sampleRateIn = sampleRateIn;
96131
this.sampleRateOut = sampleRateOut;
97132

98-
nSamplesIn = sampleRateIn / 100; // 10 ms frame
99-
nSamplesOut = sampleRateOut / 100;
133+
nSamplesIn = sampleRateIn / 100 * channels; // 10 ms frame
134+
nSamplesOut = sampleRateOut / 100 * channels;
100135
frameSizeIn = nSamplesIn * bytesPerFrame;
101136
frameSizeOut = nSamplesOut * bytesPerFrame;
102137

0 commit comments

Comments
 (0)