我正在使用声音效果生成器SFXR的Java端口,它涉及许多我不理解的神秘音乐代码,在与音频有关的任何事情上都是新手。我所知道的是,代码可以使用SourceDataLine对象在Java中可靠地生成和播放声音。
SDL对象使用的数据存储在byte []中。但是,简单地将其写入文件是行不通的(可能是因为缺少WAV标题,或者我认为是这样)。
但是,我下载了这个WAV读/写类:http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java/,它在写入WAV文件时添加了头信息。从SFXR获取byte []数据仍然会产生我所拥有的任何音乐播放器都无法播放的文件。
我想我必须遗漏一些东西。这是播放声音数据时的相关代码:
public void play(int millis) throws Exception {
AudioFormat stereoFormat = getStereoAudioFormat();
SourceDataLine stereoSdl = AudioSystem.getSourceDataLine(stereoFormat);
if (!stereoSdl.isOpen()) {
try {
stereoSdl.open();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
if (!stereoSdl.isRunning()) {
stereoSdl.start();
}
double seconds = millis / 1000.0;
int bufferSize = (int) (4 * 41000 * seconds);
byte[] target = new byte[bufferSize];
writeBytes(target);
stereoSdl.write(target, 0, target.length);
}
这是来自SFXR端口。这是来自WavIO类的save()文件(当然,该类中还有很多其他代码,我认为这可能值得发布,以防有人想要确切了解缓冲区数据的处理方式:
public boolean save()
{
try
{
DataOutputStream outFile = new DataOutputStream(new FileOutputStream(myPath));
// write the wav file per the wav file format
outFile.writeBytes("RIFF"); // 00 - RIFF
outFile.write(intToByteArray((int)myChunkSize), 0, 4); // 04 - how big is the rest of this file?
outFile.writeBytes("WAVE"); // 08 - WAVE
outFile.writeBytes("fmt "); // 12 - fmt
outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4); // 16 - size of this chunk
outFile.write(shortToByteArray((short)myFormat), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
outFile.write(shortToByteArray((short)myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
outFile.write(intToByteArray((int)mySampleRate), 0, 4); // 24 - samples per second (numbers per second)
outFile.write(intToByteArray((int)myByteRate), 0, 4); // 28 - bytes per second
outFile.write(shortToByteArray((short)myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
outFile.writeBytes("data"); // 36 - data
outFile.write(intToByteArray((int)myDataSize), 0, 4); // 40 - how big is this data chunk
outFile.write(myData); // 44 - the actual data itself - just a long string of numbers
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
return true;
}
我所知道的是,我有一堆数据,我希望它最终会出现在某种类型的可播放音频文件中(此时我会选择任何格式!)。将这个字节缓冲区转换为可播放文件的最佳方法是什么?或者这个字节[]不是我认为的那样?
答案 0 :(得分:4)
我没有太多机会使用Java的声音功能,因此我将您的问题用作学习练习(我希望您不介意)。您引用的关于Reading and Writing WAV Files in Java的文章与Java历史相关(1998)。另外一些关于手工构建WAV标题的事情并不适合我(它似乎有点容易出错)。由于Java是一种非常成熟的语言,我现在希望图书馆能够支持这种事情。
我能够通过在互联网上搜索示例代码片段,从字节数组构建WAV文件。这是我提出的代码(我希望它是次优的,但似乎有效):
// Generate bang noise data
// Sourced from http://www.rgagnon.com/javadetails/java-0632.html
public static byte[] bang() {
byte[] buf = new byte[8050];
Random r = new Random();
boolean silence = true;
for (int i = 0; i < 8000; i++) {
while (r.nextInt() % 10 != 0) {
buf[i] =
silence ? 0
: (byte) Math.abs(r.nextInt()
% (int) (1. + 63. * (1. + Math.cos(((double) i)
* Math.PI / 8000.))));
i++;
}
silence = !silence;
}
return buf;
}
private static void save(byte[] data, String filename) throws IOException, LineUnavailableException, UnsupportedAudioFileException {
InputStream byteArray = new ByteArrayInputStream(data);
AudioInputStream ais = new AudioInputStream(byteArray, getAudioFormat(), (long) data.length);
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));
}
private static AudioFormat getAudioFormat() {
return new AudioFormat(
8000f, // sampleRate
8, // sampleSizeInBits
1, // channels
true, // signed
false); // bigEndian
}
public static void main(String[] args) throws Exception {
byte[] data = bang();
save(data, "test.wav");
}
我希望它有所帮助。