I am working on an audio speaker project. I receive the real-time byte[] message from the front-end thus I built a speaker instance with javax.sound package to play the message.
The problem is: I get distorted audio when I change the sound channel number to 2 (that means stereo audio). Should I arrange some kind of stereo microphone or other specific devices to fix it? Or my way to initialize the speaker is not correct?
Here is the code to initialize the speaker:
import javax.sound.sampled.*;
public class SpeakerWriter {
public static final AudioFormat FORMAT_1 = new AudioFormat(44100.0f, 16, 1, true, false);
//16 is sample size in bits & 1 is channel number
private static AudioFormat format = FORMAT_1;
private SourceDataLine speakers;
public SourceDataLine getSpeakers() {
try {
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
speakers = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
speakers.open(format);
speakers.start();
}
catch(LineUnavailableException e) {
e.printStackTrace();
}
return speakers;
}
}
By the way, I reconstructed the code with Golang but the problem still exists.
Do you know for sure that your file is BigEndian? If not, I would try changing the BigEndian value to 'false'. I think this is considerably more common, and getting it wrong tends to create distortion.
Related
Good day. I'm trying to create a music identification app (like Shazam) in Flutter (I'm also new to Flutter) and I want it to run on mobile and desktop.
I have this piece of code in Java that gives me back a byte array with the time domain values in it :
File soundFile;
AudioInputStream audioStream;
AudioFormat audioFormat;
SourceDataLine sourceLine;
int check = 0;
byte[] songBytes;
DataLine.Info info;
soundFile = new File("./testWave.wav");
songBytes = new byte[(int) soundFile.length()];
audioStream = AudioSystem.getAudioInputStream(soundFile);
audioFormat = audioStream.getFormat();
info = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceLine = (SourceDataLine) AudioSystem.getLine(info);
sourceLine.open(audioFormat);
sourceLine.start();
while (check > -1) {
check = audioStream.read(songBytes, 0, songBytes.length);
}
sourceLine.drain();
sourceLine.close();
for (int i = 0; i < songBytes.length; i++) {
System.out.println(songBytes[i]);
}
I have searched and could not find any way to do this in Flutter/Dart. Can anyone please give me guidance on whats the best way of doing this in Flutter/Dart if it is possible and if not can you please advise me on the best method of doing this
Let's say your WAV header is 74 bytes long. (It will vary according to the number of sections, so really you need to parse it to determine that. But for any one source of WAV files it will often be the same number - use a hex dump to determine the offset of the data block plus 4.)
(By parsing the header you can find out other things like the sample rate and whether it's mono or stereo, etc.)
Then, if bytes is the Uint8List, you need bytes.buffer.asInt16List(74). This means: interpret the buffer backing the bytes as signed shorts, but starting at offset 74 - after the header.
var dataOffset = 74; // parse the WAV header or determine from a hex dump
var bytes = await file.readAsBytes();
var shorts = bytes.buffer.asInt16List(dataOffset);
print(shorts[0]); // the first sample of audio
print(shorts.length); // the number of audio samples
I´m learning how to play sound in java but with advanced controls.
I´ve found one problem: The javax.sound.sampled.AudioInputStream doesn´t support Mp3 files, and i´m running out of ideas to find how to get control of panning.
I managed to play an Mp3 file using javazoom.jl.player.advanced.AdvancedPlayer, but it doesn´t have a panning control, or i haven´t founded it.
My actual code opens a file, if the format is compatible with AudioInputStream, it plays only the right channel. If the format doesn´t, it plays using AdvancedPlayer.
Do you know a way to get panning control of mp3 files?
My code here:
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javax.sound.sampled.*;
import javax.swing.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class AudioPlayerExample2 {
private static final int BUFFER_SIZE = 4096;
public static void main(String[] args) throws IOException, LineUnavailableException, JavaLayerException {
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
new AudioPlayerExample2().play(fileChooser.getSelectedFile());
}
void play(File file) throws IOException, LineUnavailableException, JavaLayerException {
AudioInputStream audioStream;
try {
audioStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioStream.getFormat();
System.err.println(format.toString());
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
FloatControl pan = (FloatControl) audioLine.getControl(FloatControl.Type.PAN);
byte[] bytesBuffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = audioStream.read(bytesBuffer)) != -1) {
pan.setValue((float) (1));
audioLine.write(bytesBuffer, 0, bytesRead);
}
audioLine.drain();
audioLine.close();
audioStream.close();
} catch (UnsupportedAudioFileException e) {
FileInputStream fis = new FileInputStream(file);
AdvancedPlayer player = new AdvancedPlayer(fis);
player.play();
}
}
}
Panning and volume controls are system dependent and can sometimes be a bit flaky even if they are in place. For example, if you change the volume or pan setting too much at once, the discontinuity causes a click.
One solution is to go in there on a per-frame basis and make the changes yourself. For example, see "Manipulating the Audio Data Directly" at the end of the tutorial Processing Audio with Controls.
For an example, check out the code from the next tutorial on the trail: Using Files and Format Converters. Look under the heading "Reading Sound Files" and look for the comment in the code "\ Here, do something useful..."
I invite you to also take a look at the code I wrote and have made available, a class called AudioCue that has real time panning as well as real time volume and pitch playback controls. I've added smoothing (1024 steps for panning changes) to help mitigate the possibility of discontinuities.
It will be up to you to take the mp3 file and decode it into an array of audio data. I think that the javazoom libraries made available on github should give you enough code access to figure out how to do this (I did it for ogg/vorbis decoding). Once you have a float array of audio data (stereo, signed floats ranging from -1 to 1), this can be directly loaded into AudioCue.
First of all, thanks to Andrew Thompson and Phil Freihofner, I feel very good about being part of this community and having someone to trust. You really make feel happy :)
I leave here the full code that does exactly what I wanted.
As the JavaZoom MP3 SPI Documentation says: Make sure that JLayer, Tritonus and MP3SPI librairies are available in your CLASSPATH.
import javax.sound.sampled.*;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException,
UnsupportedAudioFileException, LineUnavailableException {
JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
String path = chooser.getSelectedFile().getAbsolutePath();
System.err.println(path);
File file = new File(path);
AudioInputStream baseStream = AudioSystem.getAudioInputStream(file);
AudioFormat baseFormat = baseStream.getFormat();
System.err.println(baseFormat.toString());
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16, baseFormat.getChannels(), baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), true);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
AudioInputStream stream = AudioSystem.getAudioInputStream(format, baseStream);
SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
audioLine.start();
FloatControl pan = (FloatControl) audioLine.getControl(FloatControl.Type.PAN);
pan.setValue(1);
int BUFFER_SIZE = 4096;
byte[] buffer = new byte[BUFFER_SIZE];
int read = -1;
while((read = stream.read(buffer)) != -1){
audioLine.write(buffer, 0, read);
}
audioLine.drain();
audioLine.close();
}
}
I have been trying to manually read a wav file in Java and read an array of bytes then write to an audio buffer for playback. I am receiving playback but it is heavily distorted. Java sound supports 16 bit sample rates but not 24-bit.
I went in to Logic 9 and exported a 24-bit audio file in to 16-bit and then used with my program. Originally, the 24-bit samples would produces white noise. Now I can hear my sample but very distorted and sounds like it has been bit crushed.
Can anyone help me to get a clean signal?
I am very new to audio programming but I am currently working on a basic Digital Audio Workstation.
import javax.sound.sampled.*;
import javax.sound.sampled.DataLine.Info;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
public class AudioData {
private String filepath;
private String filepath1;
private File file;
private byte [] fileContent;
private Mixer mixer;
private Mixer.Info[] mixInfos;
private AudioInputStream input;
private ByteArrayOutputStream byteoutput;
public static void main (String [] args) {
AudioData audiodata = new AudioData();
}
public AudioData () {
filepath = "/Users/ivaannagen/Documents/Samples/Engineering Samples - Obscure Techno Vol 3 (WAV)/ES_OT3_Kit03_Gmin_130bpm/ES_OT3_Kit03_FX_Fast_Snare_Riser_Gmin_130bpm.wav";
filepath1 = "/Users/ivaannagen/Documents/Samples/dawsampletest.wav";
file = new File (filepath1);
readAudio();
}
public void readAudio () {
mixInfos = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixInfos[0]);
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
// set up an audio format.
try {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); // creates data line with class type and audio format.
SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
System.out.println("Size of data line buffer: " + source.getBufferSize());
fileContent = new byte [source.getBufferSize() / 50];
byteoutput = new ByteArrayOutputStream();
input = AudioSystem.getAudioInputStream(file);
int readBytes = 0;
while ((readBytes = input.read(fileContent, 0, fileContent.length)) != -1) {
byteoutput.write(fileContent, 0, readBytes);
}
System.out.println("Size of audio buffer: " + fileContent.length);
//byteoutput.write(0);
// byteoutput.write(0);
System.out.println("Size of audio buffer: " + byteoutput.size());
source.open(format, source.getBufferSize()); // line must be open to be recognised by the mixer.
Line[] lines = mixer.getSourceLines();
System.out.println("mixer lines: " + lines.length);
// for(byte bytes: fileContent) {
// System.out.println(bytes);
// }
Thread playback = new Thread () {
public void run () {
// System.out.println((byteoutput.size() +2) % 4);
source.start(); // play (buffer originally empty)
source.write(byteoutput.toByteArray(), 0, byteoutput.size()); // write input bytes to output buffer
} // end run (to do).
}; // end thread action
playback.start(); // start thread
}
catch (LineUnavailableException lue) {
System.out.println(lue.getMessage());
}
catch (FileNotFoundException fnfe) {
System.out.println(fnfe.getMessage());
}
catch(IOException ioe) {
System.out.println(ioe.getMessage());
}
catch(UnsupportedAudioFileException uafe) {
System.out.println(uafe.getMessage());
}
}
}
Whether or not you can load and play a 24-bit file is system dependent, afaik.
I use Audacity for conversions. You should be able import your file into Audacity and export it as 16-bit, stereo, little-endian, 44100 fps, and then load that export with Java's AudioInputStream.
What you hear when playing from Audacity or from Java should be pretty much identical (adjusting for volume). If not, the most likely reason probably pertains to a mistake or overlook in the code, which is very easy to do.
The use of a ByteOutputStream in your code is superfluous. Read from the AudioInputStream into a fixed-size byte array (size being the buffer length, I recommend trying 8 or 16 * 1024 bytes as a first try) and then use the SourceDataLine write method to ship that array.
Following is code that works on my system for loading a playing a "CD Quality" wav called "a3.wav" that I have that is in the same directory as the Java class. You should be able to swap in your own 44100, 16-bit, stereo, little-endian wav file.
I've commented out an attempt to load and play a 24-bit wav file called "spoken8000_24.wav". That attempt gave me an IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 8000.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian is supported.
I have to admit, I'm unclear if my system doesn't provide the needed line or if I might have coded the format incorrectly! My OS can certainly play the file. So I'm thinking there is a distinction between what an OS can do and what a "Mixer" on a given system provides to Java.
As a get-around, I just always convert everything to "CD Quality" format, as that seems to be the most widely supported.
public class TriggerSound_SDL extends JFrame
{
public TriggerSound_SDL()
{
JButton button = new JButton("Play Sound");
button.addActionListener(e -> new Thread(() -> playBuzzer()).start());
getContentPane().add(button);
}
private void playBuzzer()
{
try
{
URL url;
url = getClass().getResource("a3.wav");
// url = getClass().getResource("spoken8000_24.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
System.out.println(ais.getFormat());
AudioFormat audioFmt;
// "CD Quality" 44100 fps, 16-bit, stereo, little endian
audioFmt = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
44100, 16, 2, 4, 44100, false);
// 8000 fps, 32-bit, stereo
// audioFmt = new AudioFormat(
// AudioFormat.Encoding.PCM_SIGNED,
// 8000, 24, 2, 6, 8000, false);
Info info = new DataLine.Info(SourceDataLine.class,
audioFmt);
SourceDataLine sdl = (SourceDataLine)AudioSystem.getLine(info);
int bufferSize = 16 * 1024;
byte[] buffer = new byte[bufferSize];
sdl.open(audioFmt, bufferSize);
sdl.start();
int numBytesRead = 0;
while((numBytesRead = ais.read(buffer)) != -1)
{
sdl.write(buffer, 0, numBytesRead);
}
}
catch (IOException | UnsupportedAudioFileException
| LineUnavailableException ex)
{
ex.printStackTrace();
}
}
private static void createAndShowGUI()
{
JFrame frame = new TriggerSound_SDL();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
This code, with some small tweaks should let you at least test the different formats.
EDIT:
I'm seeing where your goal is to make a DAW!
In that case, you will want to convert the bytes to PCM data. Can I suggest you borrow some code from AudioCue? I basically wrote it to be a Clip-substitute, and part of that involved making the PCM data available for manipulation. Some techniques for mixing, playing back at different frequencies, multithreading can be found in it.
Thanks for all the advice guys. I will be getting rid of the ByteOutputStream and just use the AudioInputStream, I now understand what I was doing was unnecessary!! Thanks for the advice all! I have indeed tried using AudioCue but it is not low level enough for what I want to do!
One more thing guys. Previously, I created a multitrack media player which is using the Clip class. To play all the audio tracks together, I was looping through a list of Clips and playing them. However, this means that all tracks may be playing a tiny amount after each other due to the processing of the loop. Also, Clip class created a new thread per audio. I do not wants 100 threads running on 100 tracks, I want one thread for my audio output. I am still trying to work out how to start all tracks at the same time without a loop....(im guessing AudioCue have nailed the concurrent cues).
Does anyone know the best way to play multiple audio tracks in to one output? Do I need to route/bus all my audio tracks in to one output and somehow write all data from audio files in to one output buffer then play this output in a thread?
Thanks!!
This question is probably not even going to be asked correctly, but I promise you I'm doing my best. Here's the scenario: I wrote a little Java app that receives an audio stream from a server. Now, when I redirect the binary stream to a file, and pipe that file to mplayer, the audio is played correctly. What I want now though is to play the audio from my own app. Here's what I got so far:
The codec mplayer uses to play the stream:
AUDIO: 22050 Hz, 2 ch, s16le, 0.0 kbit/0.00% (ratio: 0->88200)
Selected audio codec: [ffaac] afm: ffmpeg (FFmpeg AAC (MPEG-2/MPEG-4 Audio))
What I coded so far:
public class StreamPlayer {
public final AudioFormat audioFormat;
public final DataLine.Info info;
public final SourceDataLine soundLine;
public StreamPlayer() throws LineUnavailableException {
audioFormat = new AudioFormat(22050, 16, 2, true, true);
info = new DataLine.Info(SourceDataLine.class, audioFormat, 1500);
soundLine = (SourceDataLine) AudioSystem.getLine(info);
}
public void startSoundLine() throws LineUnavailableException {
soundLine.open(audioFormat);
soundLine.start();
}
public void playStream(byte[] buffer) {
soundLine.write(buffer, 0, buffer.length);
}
}
and I call the playStream function for every received packet. No errors are reported, and no sound is heard. Am I even close to doing this, or off by a long shot?
P.S. I found some third party libraries on google, but I'd really like to keep them as a last resort.
Thank you!
Get your Bytes into a ByteArrayInputStream and feed this into the streamplayer instead...
ByteArrayInputStream audioStream = new ByteArrayInputStream(buffer);
I have this simple code to concatenate two wav files. Its pretty simple and the code runs without any errors. But there is a problem with the output file. The output file generated does not play, and surprisingly its size is only 44 bytes whereas my input files "a.wav" & "b.wav" are both more than 500Kb in size.
Here is my code:
import java.io.File;
import java.io.IOException;
import java.io.SequenceInputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
public class WavConcat {
public static void main(String[] args) {
String wFile1 = "./sounds/a.wav";
String wFile2 = "./sounds/b.wav";
try {
AudioInputStream clip1 = AudioSystem.getAudioInputStream(new File(wFile1));
AudioInputStream clip2 = AudioSystem.getAudioInputStream(new File(wFile2));
AudioInputStream appendedFiles =
new AudioInputStream(
new SequenceInputStream(clip1, clip2),
clip1.getFormat(),
clip1.getFrameLength() + clip2.getFrameLength());
AudioSystem.write(appendedFiles,
AudioFileFormat.Type.WAVE,new File("./sounds/ab.wav"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Try this kind of structure. This worked for me
List audioInputStreamList = new ArrayList();
String wFile1 = "./sounds/a.wav";
String wFile2 = "./sounds/b.wav";
AudioInputStream audioInputStream1 = AudioSystem.getAudioInputStream(new File(wFile1));
AudioInputStream audioInputStream2 = AudioSystem.getAudioInputStream(new File(wFile2));
audioInputStreamList.add(audioInputStream1);
audioInputStreamList.add(audioInputStream2);
AudioFormat audioFormat = audioInputStream1.getFormat(); // audioInputStream2 format also same
AudioInputStream udioInputStream = new SequenceAudioInputStream(audioFormat,audioInputStreamList);
AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE,new File("./sounds/ab.wav"));
UPDATE
check for SequenceAudioInputStream
clip1.getFormat() returns-->
MPEG2L3 24000.0 Hz, unknown bits per sample, mono, unknown frame size, 41.666668 frames/second
clip2.getFormat() returns-->
MPEG2L3 24000.0 Hz, unknown bits per sample, mono, unknown frame size, 41.666668 frames/second
That is an odd format. I can imagine the 'unknown bits per sample' is causing a problem, but also the MPEG2L3, since JavaSound has no inbuilt encoder for MP3. It seems like they are not encoded properly. Try loading them in sound editing software and save them as a type of WAV or AU that Java Sound can understand 'out of the box'. Hopefully the editing software:
Can understand the broken MP3, and..
Will write a valid WAV or AU.
If you can convert them to 8 bit mono & 8KHz during the conversion, it might reduce the byte[] size by a factor of 6 to 1. 8KHz is considered good enough to understand speech, and for this use you need to serve the bytes of the combined sound out to the browser - so reducing it in size is crucial.