Reputation: 43
In an application I developed with Java 8, I am trying to convert a .wav audio file received in Base64 format to PCM_SIGNED format. The code works fine in the local environment, but I get the following error on JBoss EAP 6.4:
java.lang.RuntimeException: Audio processing failed
at com.assistt.voicecloneservice.service.AudioConverter.convertBase64ToPcmSigned(AudioConverter.java:42)
at com.assistt.voicecloneservice.service.AudioProcessingService.processAndUploadAndInsert(AudioProcessingService.java:35)
at com.assistt.voicecloneservice.api.TtsTextCheckService.doPost(TtsTextCheckService.java:57)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:231)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149)
at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:150)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:559)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:854)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:926)
at java.lang.Thread.run(Thread.java:750)
Caused by: javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input stream
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1121)
at com.assistt.voicecloneservice.service.AudioConverter.convertBase64ToPcmSigned(AudioConverter.java:20)
... 18 more
The method I get error is as follows:
public void convertBase64ToPcmSigned(String base64Audio) throws Exception {
byte[] audioByte = Base64.getDecoder().decode(base64Audio);
try (InputStream inputStream = new ByteArrayInputStream(audioByte);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// Get AudioInputStream from the input stream
AudioInputStream inputAudioStream = AudioSystem.getAudioInputStream(inputStream);
// Define the target audio format
AudioFormat sourceFormat = inputAudioStream.getFormat();
AudioFormat targetFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, // PCM_SIGNED format
8000.0f, // Sample rate
16, // Sample size in bits
1, // Channels (Mono)
2, // Frame size
8000.0f, // Frame rate
false // Big-endian
);
AudioInputStream convertedAudioStream = AudioSystem.getAudioInputStream(targetFormat, inputAudioStream);
File outputFile = new File("output.wav");
AudioSystem.write(convertedAudioStream, AudioFileFormat.Type.WAVE, outputFile);
} catch (UnsupportedAudioFileException | IOException e) {
throw new RuntimeException("Audio processing failed", e);
}
}
My pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.assistt</groupId>
<artifactId>VoiceCloneService</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.1</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
</dependencies>
</project>
I manually converted the base64 part to a wav file myself and it came out with the following properties.
Encoding : PCM_SIGNED
Sample Rate : 40000.0 Hz
Sample Size : 16 bits
Channels : 1
Frame Size : 2 bytes
Frame Rate : 40000.0 frames/second
Big Endian : false
The error only occurs in JBoss EAP 6.4 environment. When I test locally, the files are processed and converted correctly.
What points should I check or what alternative solutions should I try to solve this problem? Is there any special configuration required to process audio files in JBoss environment?
I tried the following code to first convert the Base64 format data to a .wav file and then process it file-based:
File file = new File(sourceFilePath);
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
if (!inputStream.markSupported()) {
return;
}
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream);
AudioFormat sourceFormat = audioInputStream.getFormat();
AudioFormat targetFormat = new AudioFormat(
AudioFormat.Encoding.ALAW,
8000,
8,
sourceFormat.getChannels(),
1,
8000,
false
);
AudioInputStream convertedAudioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);
AudioSystem.write(convertedAudioInputStream, AudioFileFormat.Type.WAVE, new File(destinationFilePath));
inputStream.close();
audioInputStream.close();
convertedAudioInputStream.close();
I got the following error on this line: AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream);
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1189)
at com.assistt.voicecloneservice.service.AudioConverter.convertBase64ToPcmSigned(AudioConverter.java:34)
...
Upvotes: 0
Views: 62
Reputation: 43
After some effort I found the solution to the problem. JBoss manages libraries and packages with its own module structure. So, even “standard Java libraries” sometimes need an additional module definition. I didn't have sound
and main
folders under /opt/jboss-eap-6.4/modules/system/layers/base/javax
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="javax.sound">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<dependencies>
<system export="true">
<paths>
<path name="javax/sound/sampled"/>
<path name="com/sun/media/sound"/>
</paths>
</system>
<module name="javax.api" export="true"/>
<module name="sun.jdk" export="true"/>
</dependencies>
</module>
In doing so, we are in a way telling JBoss “I need the classes inside the javax.sound package, I also need access to sub-packages like com.sun.media.sound and javax.sound.sampled.
2.In the WEB-INF folder of your application , add a jboss-deployment-structure.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="javax.sound" export="true"/>
<module name="sun.jdk" export="true"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
It is not enough to define a module in JBoss; you also need to specify that the application should refer to this module (dependency). Jboss adds these to the classloader.
After these steps the UnsupportedAudioFileException
error was resolved.
Upvotes: 0
Reputation: 7910
AudioSystem.getAudioInputStream()
is an overridden method. You can call it with an argument of type File
, URL
or InputStream
. The version you are using has the following note in the API
The implementation of this method may require multiple parsers to examine the stream to determine whether they support it. These parsers must be able to mark the stream, read enough data to determine whether they support the stream, and, if not, reset the stream's read pointer to its original position. If the input stream does not support these operation, this method may fail with an IOException.
I think the key fact here is in the line "The implementation...may require..." This would explain why this command works in some situations and not others.
As a work-around, I suggest using the overload with URL
as the argument. The stackoverflow wiki for javasound has examples.
URL
seems to me to work in the widest range of circumstances. For example, some people use File
as their overload, but this fails when they deploy the code in a jar (if the File
is part of the jar). Also, the mark/reset capability tests are bypassed.
Upvotes: 0