Java – 阅读,操作和编写WAV文件
在Java程序中,读取audio文件( WAV文件)到数组数组( float[]
, short[]
,…),以及从数组数组中写入WAV文件的最佳方法是什么?
关于你想获得什么的更多细节将会有所帮助。 如果原始的WAV数据对你来说是好的,只需使用一个FileInputStream,并可能是一个扫描仪把它变成数字。 但是,让我试着给你一些有意义的示例代码,让你开始:
为此,有一个名为com.sun.media.sound.WaveFileWriter的类。
InputStream in = ...; OutputStream out = ...; AudioInputStream in = AudioSystem.getAudioInputStream(in); WaveFileWriter writer = new WaveFileWriter(); writer.write(in, AudioFileFormat.Type.WAVE, outStream);
你可以实现你自己的AudioInputStream,它可以将你的数组变成audio数据。
writer.write(new VoodooAudioInputStream(numbers), AudioFileFormat.Type.WAVE, outStream);
正如@stacker提到的,当然你应该熟悉API。
如果您需要访问实际的样本值,则javax.sound.sample包不适合处理WAV文件。 该软件包可以让你改变音量,采样率等,但是如果你想要其他的效果(比如添加一个回声),你自己就可以了。 (Java教程提示,应该可以直接处理样本值,但技术编写者过分的压力。)
这个网站有一个简单的类来处理WAV文件: http : //www.labbookpages.co.uk/audio/javaWavFiles.html
我通过AudioInputStream读取WAV文件。 Java Sound Tutorials中的以下片段效果很好。
int totalFramesRead = 0; File fileIn = new File(somePathName); // somePathName is a pre-existing string whose value was // based on a user selection. try { AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(fileIn); int bytesPerFrame = audioInputStream.getFormat().getFrameSize(); if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) { // some audio formats may have unspecified frame size // in that case we may read any amount of bytes bytesPerFrame = 1; } // Set an arbitrary buffer size of 1024 frames. int numBytes = 1024 * bytesPerFrame; byte[] audioBytes = new byte[numBytes]; try { int numBytesRead = 0; int numFramesRead = 0; // Try to read numBytes bytes from the file. while ((numBytesRead = audioInputStream.read(audioBytes)) != -1) { // Calculate the number of frames actually read. numFramesRead = numBytesRead / bytesPerFrame; totalFramesRead += numFramesRead; // Here, do something useful with the audio data that's // now in the audioBytes array... } } catch (Exception ex) { // Handle the error... } } catch (Exception e) { // Handle the error... }
写一个WAV,我觉得很棘手。 在表面上看起来像一个循环的问题,写入的命令依赖于一个AudioInputStream作为参数。 但是,如何将字节写入AudioInputStream? 不应该有一个AudioOutputStream?
我发现,可以定义一个对象来访问原始audio字节数据来实现TargetDataLine。 这需要很多的方法来实现,但是大多数可以保持虚拟forms,因为它们不需要将数据写入文件。 实现的关键方法是“read(byte [] buffer,int bufferoffset,int numberofbytestoread)”。 由于这个方法可能会被多次调用,因此还应该有一个实例variables,指示已经进展的数据有多远,并将其作为上述“读取”方法的一部分进行更新。
如果你可以实现这个方法,那么你的对象可以用来创build一个新的AudioInputStream,而这个新的AudioInputStream又可以用于:
AudioSystem.write(yourAudioInputStream, AudioFileFormat.WAV, yourFileDestination)
提醒一下,可以使用TargetDataLine作为源创buildAudioInputStream。
对于直接操作数据,我在上面代码片段的最里面的循环(“audioBytes”)的缓冲区中对数据做了很好的处理。 在内部循环中,可以将字节转换为整数或浮点数,然后乘以“音量”(范围从0.0到1.0),然后将其转换回小尾数字节。 我相信,由于可以访问缓冲区中的一系列样本,因此在该阶段也可以使用各种forms的DSP过滤algorithm。 我会说,我发现最好是直接对缓冲区中的数据进行音量变化,因为您可以尽可能减小增量:每个样本增加一个增量,最大限度地减less由音量引起的不连续点击的几率。 我发现Java提供的卷的“控制线”倾向于卷的跳跃会导致点击的情况,我相信这是因为增量只能在单个缓冲区读取的粒度上实现(通常在一个范围内每1024个样本改变一次),而不是将这个变化分成更小的片段,每个样本加1个。 但是,我并不完全了解音量控制是如何实施的,所以请用一点盐做出这个猜想。
总之,Java.Sound一直是一个非常头痛的问题。 我错误的教程不包括直接从字节写入文件的明确示例。 在“如何转换…”部分中,我错误地指导了如何embedded播放文件编码的最佳示例。 然而,在这个教程中有很多宝贵的免费信息,我非常感谢! 啊,程序员的生活。 像这样的论坛和友善的帮助人员是巨大的,这是Java,这个社区如此伟大的一部分。
PS,凯,谢谢你的一本很棒的书! 我在咨询教程时会定期咨询Core Java。
WAV文件规范https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
有一个API为您的目的http://code.google.com/p/musicg/
这是直接写入wav文件的源代码。 你只需要知道math和声音工程来产生你想要的声音。 在这个例子中,该公式计算双耳节拍。
import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; public class Example { public static void main(String[] args) throws IOException { double sampleRate = 44100.0; double frequency = 440; double frequency2 = 90; double amplitude = 1.0; double seconds = 2.0; double twoPiF = 2 * Math.PI * frequency; double piF = Math.PI * frequency2; float[] buffer = new float[(int) (seconds * sampleRate)]; for (int sample = 0; sample < buffer.length; sample++) { double time = sample / sampleRate; buffer[sample] = (float) (amplitude * Math.cos((double)piF *time)* Math.sin(twoPiF * time)); } final byte[] byteBuffer = new byte[buffer.length * 2]; int bufferIndex = 0; for (int i = 0; i < byteBuffer.length; i++) { final int x = (int) (buffer[bufferIndex++] * 32767.0); byteBuffer[i] = (byte) x; i++; byteBuffer[i] = (byte) (x >>> 8); } File out = new File("out10.wav"); boolean bigEndian = false; boolean signed = true; int bits = 16; int channels = 1; AudioFormat format; format = new AudioFormat((float)sampleRate, bits, channels, signed, bigEndian); ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer); AudioInputStream audioInputStream; audioInputStream = new AudioInputStream(bais, format,buffer.length); AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, out); audioInputStream.close(); } }
如果你可以修改这个创build一个嘻哈低音,这将是很酷,因为这是我正在尝试修改这个程序做。
首先,您可能需要知道WAVE结构的标题和数据位置,您可以在这里find该规范。 请注意,数据是小端的。
有一个API可以帮助你实现你的目标。
Wave文件由javax.sound.sample包支持
既然不是一个简单的API,你应该阅读一篇介绍API的文章/教程
Java声音,介绍
我用一些神奇的FileInputStream
:
byte[] byteInput = new byte[(int)file.length() - 44]; short[] input = new short[(int)(byteInput.length / 2f)]; try{ FileInputStream fis = new FileInputStream(file); fis.read(byteInput, 44, byteInput.length - 45); ByteBuffer.wrap(byteInput).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(input); }catch(Exception e ){ e.printStackTrace(); }
你的样本值short[] input
!