How to fade a .wav file in Java? - java

Hey I wrote a program which cuts a specific area from a wav file.
But I realized that the cut is very hard so I wanted to fade it in and out. My problem is that I have no idea how to achieve that in java because I'm very new to the sound library from java.
Could someone give a hint or a tip how to achieve that or tip for a resource where I can find the answer?
Here is some code I wrote before:
AudioInputStream in = null;
AudioInputStream out = null;
File originalFile = new File(filePath);
if (originalFile.exists() && originalFile.isFile())
{
File editedFile = new File(newPath);
try
{
in = AudioSystem.getAudioInputStream(originalFile);
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(originalFile);
AudioFormat format = fileFormat.getFormat();
int bytesPerSecond = format.getFrameSize() * (int) format.getFrameRate();
in.skip(start * bytesPerSecond);
long framesOfAudioToCopy = trackDuration * (int) format.getFrameRate();
// out is the audiostream which contains the output wav file
out = new AudioInputStream(in, format, framesOfAudioToCopy);
// so I guess here would be the right place to fade the audio file
// just before writing it to the disk
AudioSystem.write(out, fileFormat.getType(), editedFile);
System.out.println("Trimming done!");
System.out.println();
}
catch (UnsupportedAudioFileException e)
{
errorMessage = e.getMessage();
}
catch (IOException e)
{
errorMessage = e.getMessage();
}

You can try using the controls provided in javax.sound.sampled, a tutorial is here, but I've never had much luck with them. My experience is that if they exist for a given system (pc/os dependent) there are still issues as the volume changes only occur at buffer boundaries.
Note that the very last part of the tutorial suggests manipulating the audio directly. To do this requires multiple steps.
1) get a hold of the individual bytes of the sound file
There is a code example of this in the very next tutorial on Using Files and Format Converters, the section "reading sound files". In this code example, note the point where we have a comment marking the point where access to the individual bytes has been provided:
// Here, do something useful with the audio data that's
// now in the audioBytes array...
2) Convert bytes to PCM (depends on the audio format)
3) multiply by a volume factor
4) increment or decrement the factor (if fading)
5) convert the PCM back to bytes
6) pack and ship via a SourceDataLine (again depends on audio format)
All the steps have been described before in greater detail in StackOverflow and should be searchable, though I don't know how easy it will be at this point to find them.
There are a couple free libraries that will allow real-time volume fading. I wrote AudioCue for this (and real time frequency and panning) and there is also TinySound.
PS I am happy to answer questions and take suggestions for improvements in presentation for the library I wrote.

Related

Specific wav file failing to load while others load without errors

I'm working on a system to play, pause and stop music. I'm testing this out with 2 different wav files, one with a length of 45 seconds and other with a length of 3:35 minutes. The problem I'm having is that the 45 second wav file plays without any problem. The 3:35 minute wav file on the other hand, doesn't load. Is there a maximum time limit to wav files in java or is it possible the wav file is broken? It plays without any problem on windows app "groove music".
I've searched around on stack overflow but no one seemed to experience the same problem as I am, one wav file playing, the other one not.
Error code I'm getting:
javax.sound.sampled.LineUnavailableException: line with format PCM_FLOAT 44100.0 Hz, 32 bit, stereo, 8 bytes/frame, not supported.
The method i use for playing the wav file.
public static void playAudio(String name) {
try {
System.out.println("NOTE: Playing audio");
clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Engine.class.getResourceAsStream("/Audio/" + name));
clip.open(inputStream);
clip.start();
} catch(Exception e) {
System.out.println("ERROR: Failed to load audio");
}
}
Calling the method
Engine.playAudio("easy2.wav");
Picture of the wav files in the "src/Audio/" folder
Given the error message, one thing you could try is after opening the AudioInputStream from the resource, do the following:
AudioFormat fmt = inputStream.getFormat();
fmt = new AudioFormat(fmt.getSampleRate(),
16,
fmt.getChannels(),
true,
true);
inputStream = AudioSystem.getAudioInputStream(fmt, inputStream);
This attempts to convert the stream away from a floating-point format, hopefully to something that the system is more likely to support. (Also see JavaDoc for AudioSystem.getAudioInputStream(AudioFormat, AudioInputStream).)
You can run the following code to find out what formats are likely available for playback:
Arrays.stream(AudioSystem.getSourceLineInfo(new Line.Info(SourceDataLine.class)))
.filter(info -> info instanceof DataLine.Info)
.map(info -> (DataLine.Info) info)
.flatMap(info -> Arrays.stream(info.getFormats()))
.forEach(System.out::println);
If the above method of converting the audio stream doesn't work, then probably your best bet is to just convert the file with some editor, such as Audacity. Java sound is unfortunately still pretty limited in what formats it supports by default. (The most reliable format is probably CDDA, which is 44100Hz, 16-bit signed LPCM.)
There may also be 3rd-party SPIs which support conversions from floating-point PCM.
The problem I was facing originated in the wav file itself. Since a wav file can have one of several different formats, the one it was encoded with was not compatible with java. The solution was to change the bitrate and Hz of the wav file to match an encoding that java supported.
More about wav formats: https://en.wikipedia.org/wiki/WAV

Keeping Encoding when Reading Image File

I'm am currently reading through a file which contains meta-data and a tiff image like so:
private String readFile( String file ) throws IOException {
File file = new File(filename);
int size = (int) file.length();
byte[] bytes = new byte[size];
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
buf.read(bytes, 0, bytes.length);
buf.close();
...
}
I parse the meta-data + image content, then I try to output the tiff like this, where img is a String:
writer = new BufferedWriter( new FileWriter( "img.tiff"));
writer.write(img);
writer.close();
Why is the encoding being lost of the tiff image file?
Why are you trying to rewrite the file?
If the answer is "I'm trying to alter some metadata within the file." I strongly suggest that you use a set of tools that are specifically geared towards working with TIFF metadata, especially if you intend to manipulate/alter than metadata as there are several special case data elements in TIFF files that really don't like being moved around blithely.
My day-to-day job involves understanding the TIFF spec, so I always get a little antsy when I see people mucking around with the internals of TIFFs without first consulting the spec or being concerned with some of the bizarre special cases that exist in the wild that now need to be handled because of someone else who didn't fully grok the spec and created a commercial product that generated thousands of these beasts (I'm looking at you Microsoft for making "old style JPEG compression" TIFFs, but I've also seen a Java product that defined a type of image that used floating point numbers for the component values without bothering to (1) normalize them as the spec would have you do or (2) have a standard for defining what the expected min and max of the component values would be).
In my code base (and this is a commercial product), you can do your work like this:
TiffFile myTiff = new TiffFile();
myTiff.read(someImageInputStream);
for (TiffDirectory dir : myTiff.getImages())
{
// a TiffDirectory contains a collection of TiffTag objects, from which the
// metadata for each image in the document can be read/edited
// TiffTag definitions can be found [here][2].
}
myTiff.save(someImageOutputStream); // writes the whole TIFF back
and in general, we've found that it's really advanced customers who want to do this. For the most part, we find that customers are more concerned with higher-level operations like combining TIFF files into a single document or extracting out pages, for which we have a different API which is much lighter weight and doesn't require you to know the TIFF specification (as it should be).
Try specifying the encoding in your writer.
http://docs.oracle.com/javase/7/docs/api/java/io/OutputStreamWriter.html#OutputStreamWriter%28java.io.OutputStream,%20java.nio.charset.CharsetEncoder%29
Wrap your stream:
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
For images you should look into the ImageIO package.
http://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html#getImageWriter%28javax.imageio.ImageReader%29

jFLAC. Split FLAC File

I need to split flac file to many pieces. I am using jFLAC library to read flac files
FLACDecoder decoder = new FLACDecoder(inputStream);
then I am trying to decode parent file between to SeekPoints
decoder.decode(seekPointFrom, seekPointTo);
I also don't quite understand how properly to get this seekpoints for seconds value. For example I need first seekpoint from 0 seconds and second to 150 seconds. How to get right seek points objects? Seekpoint cinstructor is
/**
* The constructor.
* #param sampleNumber The sample number of the target frame
* #param streamOffset The offset, in bytes, of the target frame with respect to beginning of the first frame
* #param frameSamples The number of samples in the target frame
*/
public SeekPoint(long sampleNumber, long streamOffset, int frameSamples) {
this.sampleNumber = sampleNumber;
this.streamOffset = streamOffset;
this.frameSamples = frameSamples;
}
also decoder have some listener that listen every read chunk action.
#Override
public void processPCM(ByteData pcm) {
try {
outputStream.write(pcm.getData(), 0, pcm.getLen());
} catch (IOException e) {
e.printStackTrace();
}
}
When writing is done I am tying to play new flac file but my player alerts that file incorrect. What I need to do that my flac files will open right? Maybe I need to write some header to this file or something else?
In regards to the FLAC SeekPoints, there is no guarantee that there will be one that corresponds to a given second - there might only be a few SeekPoints in the entire audio file.
As such, I recently updated jFLAC with a seek function to get you to at least the closest audio frame:
https://github.com/nguillaumin/jflac/commit/585940af97157eb11b60c15cc8cb13ef3fc27ce3
In regards to writing out a new file, the decoded data will be in raw PCM samples. So you will need to pipe it into a FLAC encoder if you want a valid FLAC file as the output. Alternately you could write out a Wave header and dump the raw PCM samples, then convert that resultant Wave file into a FLAC file.

Looking for pure Java API to read metadata from an MP4 file [duplicate]

Mp3 files can be handled using this mp3 SPI support, but I'm not finding something similar to mp4 files.
Any help would be appreciated.
--UPDATE
What I want to do is get the file's size, as I do with wave files using this code:
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioInputStream.getFormat();
long audioFileLength = file.length();
int frameSize = format.getFrameSize();
float frameRate = format.getFrameRate();
float durationInSeconds = (audioFileLength / (frameSize * frameRate));
--ANSWER
Here is the answer code using the hint of #mdma (IBM toolkit):
/**
* Use IBMPlayerForMpeg4SDK to get mp4 file duration.
*
* #return the mp4File duration in milliseconds.
*/
public static long getMp4Duration(File mp4File) throws IllegalStateException, IOException {
PlayerControl playerControl = PlayerFactory.createLightweightMPEG4Player();
playerControl.open(mp4File.getAbsolutePath());
long mili = playerControl.getDuration();
// int sec = (int) ((mili / 1000) % 60);
// int min = (int) ((mili / 1000) / 60);
// System.out.println("IBM Tookit result = " + min + ":" + sec);
return mili;
}
--
Related, language independent, question:
Anyone familiar with mp4 data structure?
Mp4 is a container format - to be able to find the duration of the audio inside, you have to first parse the content out of the container. You can extract the content of an mp4 file using isobox mp4parser.
Once you've done that, you then have the raw audio data. If it's one of the supported formats in java (wav, mp3, etc..) then you can just open this file like you have done for wavs already. Initially you will probably extract the audio to a separate file, for simplicity's sake and easier debugging. When this is working, you can then do the extraction inline - you implement an InputStreamFilter that extracts the audio content from the mp4 on the fly, so no additional external files are required.
IBM Alphaworks have a pure java MP4 decoder library available, but it's possibly overkill for your present needs.
Xuggler ( http://www.xuggle.com/xuggler/ ) provides about the best Java wrapper for FFMPEG that I've seen - it'll let you decode the images out of almost any file, and then do whatever you like with them.
You don't specify what you want to do. If you just want to play the files, you can use MPlayer and control it remotely via the ProcessBuilder API and stdio.

Java AudioSystem and TargetDataLine

I am trying to capture audio from the line-in from my PC, to do this I am using AudioSystem class. There is one of two choices with the static AudioSystem.write method: Write to a file Or Write to a stream. I can get it to write to a file just fine, but whenever I try to write to a stream I get thrown java.io.IOException (stream length not specified). As for my buffer I am using a ByteArrayOutputStream. Is there another kind of stream I am supposed to be using or messing up somewhere else?
Also in a related subject, one can sample the audio line in (TargetDataLine) directly by calling read. Is this the preferred way doing audio capture or using AudioSystem?
Update
Source code that was requested:
final private TargetDataLine line;
final private AudioFormat format;
final private AudioFileFormat.Type fileType;
final private AudioInputStream audioInputStream;
final private ByteArrayOutputStream bos;
// Constructor, etc.
public void run()
{
System.out.println("AudioWorker Started");
try
{
line.open(format);
line.start();
// This commented part is regarding the second part
// of my question
// byte[] buff = new byte[512];
// int bytes = line.read(buff, 0, buff.length);
AudioSystem.write(audioInputStream, fileType, bos);
}
catch ( Exception e )
{
e.printStackTrace();
}
System.out.println("AudioWorker Finished");
}
// Stack trace in console
AudioWorker Started
java.io.IOException: stream length not specified
at com.sun.media.sound.WaveFileWriter.write(Unknown Source)
at javax.sound.sampled.AudioSystem.write(Unknown Source)
at AudioWorker.run(AudioWorker.java:41)
AudioWorker Finished
From AudioSystem.write JavaDoc:
Writes a stream of bytes representing an audio file of the specified file type to the output stream provided. Some file types require that the length be written into the file header; such files cannot be written from start to finish unless the length is known in advance. An attempt to write a file of such a type will fail with an IOException if the length in the audio file type is AudioSystem.NOT_SPECIFIED.
Since the Wave format requires the length to be written at the beginning of the file, the writer is querying the getFrameLength method of your AudioInputStream. When this returns NOT_SPECIFIED—because your recording "live" data of as-yet-unspecified length— the writer throws the exception.
The File-oriented works around this by writing dummy data to the length field, then re-opening the file when the write is complete and overwriting that area of the file.
Use an output format that doesn't need the length in advance (au), or use an AudioInputStream that returns a valid frame length, or use the File version of the API.
You should check out Richard Baldwin's tutorial on Java sound. There's a complete source listing at the bottom of the article where he uses TargetDataLine's read to capture audio.
You could also try looking into using JMF which is a bit hairy but works a bit better that javax.sound.sampled stuff. There's quite a few tutorials on the JMF page which describe how to record from line in or mic channels.

Categories

Resources