├── src ├── test │ ├── resources │ │ ├── sample.wav │ │ └── logback.xml │ └── java │ │ └── com │ │ └── orctom │ │ └── rnnoise │ │ └── DenoiserIT.java └── main │ ├── resources │ ├── darwin │ │ └── librnnoise.dylib │ ├── linux-amd64 │ │ └── librnnoise.so │ └── linux-x86-64 │ │ └── librnnoise.so │ └── java │ └── com │ └── orctom │ └── rnnoise │ ├── exception │ └── IllegalFrameSizeException.java │ ├── Bytes.java │ └── Denoiser.java ├── .gitignore └── pom.xml /src/test/resources/sample.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orctom/rnnoise-java/HEAD/src/test/resources/sample.wav -------------------------------------------------------------------------------- /src/main/resources/darwin/librnnoise.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orctom/rnnoise-java/HEAD/src/main/resources/darwin/librnnoise.dylib -------------------------------------------------------------------------------- /src/main/resources/linux-amd64/librnnoise.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orctom/rnnoise-java/HEAD/src/main/resources/linux-amd64/librnnoise.so -------------------------------------------------------------------------------- /src/main/resources/linux-x86-64/librnnoise.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orctom/rnnoise-java/HEAD/src/main/resources/linux-x86-64/librnnoise.so -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | DummyTests.java 2 | DummyTest.java 3 | .gradle 4 | .idea 5 | *.iml 6 | *.ipr 7 | *.iws 8 | /files/ 9 | build/ 10 | target/ 11 | 12 | .classpath 13 | .settings/ 14 | .project 15 | 16 | *.log 17 | *tmp* 18 | diagrams 19 | *.versionsBackup 20 | *.pcm 21 | -------------------------------------------------------------------------------- /src/main/java/com/orctom/rnnoise/exception/IllegalFrameSizeException.java: -------------------------------------------------------------------------------- 1 | package com.orctom.rnnoise.exception; 2 | 3 | public class IllegalFrameSizeException extends RuntimeException { 4 | 5 | public IllegalFrameSizeException(String message) { 6 | super(message); 7 | } 8 | 9 | public IllegalFrameSizeException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [%d{yyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{35} %line - %m%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/orctom/rnnoise/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.orctom.rnnoise; 2 | 3 | public abstract class Bytes { 4 | 5 | public static byte[] toByteArray(float[] input) { 6 | byte[] ret = new byte[input.length * 2]; 7 | for (int i = 0; i < input.length; ++i) { 8 | short x = ((Float) input[i]).shortValue(); 9 | ret[i * 2] = (byte) (x & 0x00FF); 10 | ret[i * 2 + 1] = (byte) ((x & 0xFF00) >> 8); 11 | } 12 | return ret; 13 | } 14 | 15 | public static float[] toFloatArray(byte[] input) { 16 | float[] ret = new float[input.length / 2]; 17 | for (int i = 0; i < input.length / 2; ++i) { 18 | if ((input[i * 2 + 1] & 0x80) != 0) { 19 | ret[i] = -32768 + ((input[i * 2 + 1] & 0x7f) << 8) | (input[i * 2] & 0xff); 20 | } else { 21 | ret[i] = ((input[i * 2 + 1] << 8) & 0xff00) | (input[i * 2] & 0xff); 22 | } 23 | } 24 | return ret; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.orctom 6 | basepom 7 | 1.3 8 | 9 | 10 | rnnoise-java 11 | 1.1.2-SNAPSHOT 12 | 13 | rnnoise for Java 14 | https://github.com/orctom/rnnoise-java// 15 | 16 | 17 | https://github.com/orctom/rnnoise-java/issues 18 | GitHub 19 | 20 | 21 | 22 | Apache License, Version 2.0 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | repo 25 | 26 | 27 | 28 | https://github.com/orctom/rnnoise-java/ 29 | scm:git:git@github.com:orctom/rnnoise-java.git 30 | scm:git:git@github.com:orctom/rnnoise-java.git 31 | HEAD 32 | 33 | 34 | 35 | 1.8 36 | 37 | 38 | 39 | 40 | net.java.dev.jna 41 | jna 42 | 4.5.0 43 | 44 | 45 | org.slf4j 46 | slf4j-api 47 | 1.7.25 48 | 49 | 50 | com.google.guava 51 | guava 52 | 23.0 53 | test 54 | 55 | 56 | ch.qos.logback 57 | logback-classic 58 | 1.2.3 59 | test 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/test/java/com/orctom/rnnoise/DenoiserIT.java: -------------------------------------------------------------------------------- 1 | package com.orctom.rnnoise; 2 | 3 | import com.google.common.base.Stopwatch; 4 | import com.google.common.io.ByteStreams; 5 | import com.google.common.io.Files; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.File; 11 | import java.io.InputStream; 12 | import java.util.Arrays; 13 | 14 | public class DenoiserIT { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(DenoiserIT.class); 17 | 18 | @Test 19 | public void process() throws Exception { 20 | try (Denoiser denoiser = new Denoiser()) { 21 | InputStream in = DenoiserIT.class.getResourceAsStream("/sample.wav"); 22 | byte[] bytes = ByteStreams.toByteArray(in); 23 | // int frameSize = 9600; 24 | int frameSize = 12800; 25 | int startIndex = 0; 26 | int endIndex = frameSize; 27 | boolean isFistFrame = true; 28 | LOGGER.debug("total: {}", bytes.length); 29 | byte[] output = new byte[bytes.length]; 30 | while (startIndex < bytes.length) { 31 | if (endIndex > bytes.length) { 32 | endIndex = bytes.length; 33 | } 34 | byte[] data = Arrays.copyOfRange(bytes, startIndex, endIndex); 35 | LOGGER.debug("{} --> {}", startIndex, endIndex); 36 | Stopwatch stopwatch = Stopwatch.createStarted(); 37 | byte[] dataOut = denoiser.process(data); 38 | LOGGER.debug("took: {}", stopwatch); 39 | 40 | if (isFistFrame) { 41 | isFistFrame = false; 42 | startIndex += frameSize; 43 | endIndex += frameSize; 44 | continue; 45 | } 46 | System.arraycopy(dataOut, 0, output, startIndex, endIndex - startIndex); 47 | startIndex += frameSize; 48 | endIndex += frameSize; 49 | } 50 | 51 | String cwd = System.getProperty("user.dir"); 52 | File file = new File(cwd, "processed.pcm"); 53 | if (file.exists()) { 54 | boolean deleted = file.delete(); 55 | LOGGER.debug("deleted: {}", deleted); 56 | } 57 | 58 | Files.write(output, file); 59 | LOGGER.debug("Processed wrote to: {}", file.getAbsolutePath()); 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | } 63 | LOGGER.debug("done."); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/orctom/rnnoise/Denoiser.java: -------------------------------------------------------------------------------- 1 | package com.orctom.rnnoise; 2 | 3 | import com.sun.jna.Library; 4 | import com.sun.jna.Native; 5 | import com.sun.jna.Pointer; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.Closeable; 10 | import java.util.Arrays; 11 | import java.util.concurrent.atomic.AtomicBoolean; 12 | 13 | public class Denoiser implements Closeable { 14 | 15 | private static final Logger LOGGER = LoggerFactory.getLogger(Denoiser.class); 16 | 17 | private static final int FRAME_SIZE = 960; 18 | 19 | private Pointer state; 20 | 21 | private AtomicBoolean stopped = new AtomicBoolean(false); 22 | 23 | public Denoiser() { 24 | LOGGER.debug("Denoiser started"); 25 | state = Rnnoise.INSTANCE.rnnoise_create(); 26 | } 27 | 28 | public void close() { 29 | LOGGER.debug("Denoiser closed"); 30 | if (stopped.getAndSet(true)) { 31 | return; 32 | } 33 | 34 | Rnnoise.INSTANCE.rnnoise_destroy(state); 35 | state = null; 36 | } 37 | 38 | public byte[] process(byte[] pcm) { 39 | int len = pcm.length; 40 | if (len < FRAME_SIZE) { 41 | return pcm; 42 | } 43 | 44 | byte[] denoised = new byte[len]; 45 | int startIndex = 0; 46 | int endIndex = FRAME_SIZE; 47 | while (startIndex < len) { 48 | if (endIndex > len) { 49 | endIndex = len; 50 | } 51 | 52 | LOGGER.trace("start: {}, end: {}", startIndex, endIndex); 53 | 54 | byte[] frame = Arrays.copyOfRange(pcm, startIndex, endIndex); 55 | byte[] processed = processFrame(frame); 56 | System.arraycopy(processed, 0, denoised, startIndex, processed.length); 57 | startIndex += FRAME_SIZE; 58 | endIndex += FRAME_SIZE; 59 | } 60 | 61 | return denoised; 62 | } 63 | 64 | private byte[] processFrame(byte[] frame) { 65 | if (frame.length != FRAME_SIZE) { 66 | return frame; 67 | } 68 | 69 | float[] pcmIn = Bytes.toFloatArray(frame); 70 | float[] pcmOut = new float[pcmIn.length]; 71 | Rnnoise.INSTANCE.rnnoise_process_frame(state, pcmOut, pcmIn); 72 | return Bytes.toByteArray(pcmOut); 73 | } 74 | 75 | public interface Rnnoise extends Library { 76 | 77 | Rnnoise INSTANCE = (Rnnoise) Native.loadLibrary("rnnoise", Rnnoise.class); 78 | 79 | Pointer rnnoise_create(); 80 | 81 | int rnnoise_get_size(); 82 | 83 | int rnnoise_init(Pointer state); 84 | 85 | void rnnoise_process_frame(Pointer state, float[] out, float[] in); 86 | 87 | void rnnoise_destroy(Pointer stata); 88 | } 89 | 90 | } 91 | --------------------------------------------------------------------------------