Skip to content

Commit 8643bb4

Browse files
authored
Add audio buffer sample unit test (#4167)
1 parent 3c78ec6 commit 8643bb4

File tree

2 files changed

+143
-1
lines changed

2 files changed

+143
-1
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.codename1.media;
2+
3+
import com.codename1.capture.Capture;
4+
import com.codename1.io.File;
5+
import com.codename1.io.FileSystemStorage;
6+
import com.codename1.io.Util;
7+
import com.codename1.junit.FormTest;
8+
import com.codename1.junit.UITestBase;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class AudioBufferSampleTest extends UITestBase {
13+
14+
@FormTest
15+
void captureAudioRedirectsFramesToBufferAndPersistsWav() throws Exception {
16+
FileSystemStorage fs = FileSystemStorage.getInstance();
17+
implementation.clearFileSystem();
18+
implementation.clearAudioCaptureFrames();
19+
String recordingsDir = fs.getAppHomePath() + "recordings/";
20+
fs.mkdir(recordingsDir);
21+
22+
String bufferPath = "tmpBuffer.pcm";
23+
int wavSampleRate = 16000;
24+
AudioBuffer audioBuffer = MediaManager.getAudioBuffer(bufferPath, true, 8);
25+
final float[] captured = new float[audioBuffer.getMaxSize()];
26+
final int[] callbackCount = new int[1];
27+
final int[] lastSampleRate = new int[1];
28+
final int[] lastSize = new int[1];
29+
30+
WAVWriter writer = new WAVWriter(new File("tmpBuffer.wav"), wavSampleRate, 1, 16);
31+
try {
32+
audioBuffer.addCallback(new AudioBuffer.AudioBufferCallback() {
33+
public void frameReceived(AudioBuffer buffer) {
34+
if (buffer.getSampleRate() > wavSampleRate) {
35+
buffer.downSample(wavSampleRate);
36+
}
37+
buffer.copyTo(captured);
38+
lastSampleRate[0] = buffer.getSampleRate();
39+
lastSize[0] = buffer.getSize();
40+
callbackCount[0]++;
41+
try {
42+
writer.write(captured, 0, buffer.getSize());
43+
} catch (Exception e) {
44+
throw new RuntimeException(e);
45+
}
46+
}
47+
});
48+
49+
MediaRecorderBuilder options = new MediaRecorderBuilder()
50+
.audioChannels(1)
51+
.redirectToAudioBuffer(true)
52+
.path(bufferPath);
53+
54+
implementation.addAudioCaptureFrame(32000, 1, new float[]{1f, 0.5f, -0.5f, -1f});
55+
56+
Capture.captureAudio(options);
57+
58+
writer.close();
59+
60+
String copyPath = recordingsDir + "sample.wav";
61+
Util.copy(fs.openInputStream(new File("tmpBuffer.wav").getAbsolutePath()), fs.openOutputStream(copyPath));
62+
63+
assertEquals(1, callbackCount[0]);
64+
assertEquals(wavSampleRate, lastSampleRate[0]);
65+
assertTrue(lastSize[0] > 0);
66+
assertSame(options, implementation.getLastMediaRecorderBuilder());
67+
assertTrue(fs.exists(copyPath));
68+
assertTrue(fs.getLength(copyPath) > 44);
69+
} finally {
70+
MediaManager.releaseAudioBuffer(bufferPath);
71+
try {
72+
writer.close();
73+
} catch (Exception ignored) {
74+
}
75+
implementation.clearFileSystem();
76+
}
77+
}
78+
}
79+

maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
import com.codename1.payment.Purchase;
1414
import com.codename1.l10n.L10NManager;
1515
import com.codename1.location.LocationManager;
16+
import com.codename1.media.AudioBuffer;
1617
import com.codename1.media.Media;
18+
import com.codename1.media.MediaManager;
1719
import com.codename1.media.MediaRecorderBuilder;
1820
import com.codename1.messaging.Message;
1921
import com.codename1.notifications.LocalNotification;
@@ -36,6 +38,7 @@
3638
import com.codename1.ui.geom.Rectangle;
3739
import com.codename1.ui.geom.Shape;
3840
import com.codename1.util.AsyncResource;
41+
import java.io.Closeable;
3942

4043
import java.io.ByteArrayInputStream;
4144
import java.io.ByteArrayOutputStream;
@@ -175,6 +178,7 @@ public class TestCodenameOneImplementation extends CodenameOneImplementation {
175178
private String nextCaptureAudioPath = "file://test-audio.wav";
176179
private MediaRecorderBuilder lastMediaRecorderBuilder;
177180
private VideoCaptureConstraints lastVideoConstraints;
181+
private final List<AudioCaptureFrame> audioCaptureFrames = new ArrayList<AudioCaptureFrame>();
178182

179183

180184
public TestCodenameOneImplementation() {
@@ -1994,7 +1998,14 @@ public boolean exists(String file) {
19941998
public void rename(String file, String newName) {
19951999
TestFile f = fileSystem.remove(file);
19962000
if (f != null) {
1997-
fileSystem.put(newName, f);
2001+
String target = newName;
2002+
if (newName != null && !newName.contains("://") && !newName.startsWith("/")) {
2003+
int lastSlash = file.lastIndexOf('/');
2004+
if (lastSlash >= 0) {
2005+
target = file.substring(0, lastSlash + 1) + newName;
2006+
}
2007+
}
2008+
fileSystem.put(target, f);
19982009
}
19992010
}
20002011

@@ -2166,6 +2177,17 @@ public void closingOutput(OutputStream stream) {
21662177
@Override
21672178
public void cleanup(Object obj) {
21682179
cleanupCalls.add(obj);
2180+
if (obj instanceof Closeable) {
2181+
try {
2182+
((Closeable) obj).close();
2183+
} catch (IOException ignored) {
2184+
}
2185+
} else if (obj instanceof AutoCloseable) {
2186+
try {
2187+
((AutoCloseable) obj).close();
2188+
} catch (Exception ignored) {
2189+
}
2190+
}
21692191
}
21702192

21712193
@Override
@@ -3444,6 +3466,15 @@ public void captureAudio(MediaRecorderBuilder recordingOptions, ActionListener r
34443466
recordingOptions = new MediaRecorderBuilder();
34453467
}
34463468
lastMediaRecorderBuilder = recordingOptions;
3469+
if (recordingOptions.isRedirectToAudioBuffer()) {
3470+
AudioBuffer buffer = MediaManager.getAudioBuffer(recordingOptions.getPath());
3471+
if (buffer != null) {
3472+
for (AudioCaptureFrame frame : audioCaptureFrames) {
3473+
buffer.copyFrom(frame.getSampleRate(), frame.getNumChannels(), frame.getSamples());
3474+
}
3475+
}
3476+
audioCaptureFrames.clear();
3477+
}
34473478
response.actionPerformed(new ActionEvent(nextCaptureAudioPath));
34483479
}
34493480

@@ -3474,10 +3505,42 @@ public MediaRecorderBuilder getLastMediaRecorderBuilder() {
34743505
return lastMediaRecorderBuilder;
34753506
}
34763507

3508+
public void addAudioCaptureFrame(int sampleRate, int numChannels, float[] samples) {
3509+
audioCaptureFrames.add(new AudioCaptureFrame(sampleRate, numChannels, samples));
3510+
}
3511+
3512+
public void clearAudioCaptureFrames() {
3513+
audioCaptureFrames.clear();
3514+
}
3515+
34773516
public VideoCaptureConstraints getLastVideoConstraints() {
34783517
return lastVideoConstraints;
34793518
}
34803519

3520+
private static final class AudioCaptureFrame {
3521+
private final int sampleRate;
3522+
private final int numChannels;
3523+
private final float[] samples;
3524+
3525+
private AudioCaptureFrame(int sampleRate, int numChannels, float[] samples) {
3526+
this.sampleRate = sampleRate;
3527+
this.numChannels = numChannels;
3528+
this.samples = Arrays.copyOf(samples, samples.length);
3529+
}
3530+
3531+
private int getSampleRate() {
3532+
return sampleRate;
3533+
}
3534+
3535+
private int getNumChannels() {
3536+
return numChannels;
3537+
}
3538+
3539+
private float[] getSamples() {
3540+
return Arrays.copyOf(samples, samples.length);
3541+
}
3542+
}
3543+
34813544
public static final class TestFile {
34823545
final boolean directory;
34833546
final byte[] content;

0 commit comments

Comments
 (0)