下面的代码应该创建一个包含正弦波的文件。在典型的频率(220Hz, 440Hz, 880Hz)下,它表现得很好,但在许多其他频率下,它就不那么好了,比如225Hz, 883Hz等等。我该怎么做才能得到任意频率的正弦波?
import java.lang.Math;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CreateSine
{
static String fileNameString = "Sine.wav";
static File file = new File(fileNameString);
static String filePath = file.getAbsolutePath();
static RandomAccessFile raw;
static int byteCount = 0;
static double pow215 = Math.pow(2, 15);
static float freq = 440.0f;
static int sRate = 44100;
static int bitDepth = 16;
static int nChannels = 1;
static int dur = 1;
static float changeRate = (float)((2.0 * Math.PI * freq) / sRate);
public static void main(String[] args)
{
try
{
raw = new RandomAccessFile(filePath, "rw");
raw.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed
raw.writeBytes("RIFF");
raw.writeInt(0); // Final file size not known yet, write 0. This is = sample count + 36 bytes from header.
raw.writeBytes("WAVE");
raw.writeBytes("fmt ");
raw.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM
raw.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
raw.writeShort(Short.reverseBytes((short)nChannels));// Number of channels, 1 for mono, 2 for stereo
raw.writeInt(Integer.reverseBytes(sRate)); // Sample rate
raw.writeInt(Integer.reverseBytes(sRate*bitDepth*nChannels/8)); // Byte rate, SampleRate*NumberOfChannels*bitDepth/8
raw.writeShort(Short.reverseBytes((short)(nChannels*bitDepth/8))); // Block align, NumberOfChannels*bitDepth/8
raw.writeShort(Short.reverseBytes((short)bitDepth)); // Bit Depth
raw.writeBytes("data");
raw.writeInt(0); // Data chunk size not known yet, write 0. This is = sample count.
}
catch(IOException e)
{
System.out.println("I/O exception occured while writing data");
}
for (int i = 0; i < sRate*dur; i++)
{
writeSample( (float)Math.sin( i * changeRate ) );
}
closeFile();
System.out.print("Finished");
}
static void writeSample(float floatValue)
{
try
{
short hexSample = (short)((floatValue * pow215));
raw.writeShort(Short.reverseBytes(hexSample));
byteCount += 2;
}
catch(IOException e)
{
System.out.println("I/O exception occured while writing data");
}
}
static void closeFile()
{
try
{
raw.seek(4); // Write size to RIFF header
raw.writeInt(Integer.reverseBytes(byteCount + 36));
raw.seek(40); // Write size to Subchunk2Size field
raw.writeInt(Integer.reverseBytes(byteCount));
raw.close();
}
catch(IOException e)
{
System.out.println("I/O exception occured while closing output file");
}
}
}
谢谢你的帮助。
你的问题并没有真正描述问题是什么,只是说它不好。我要大胆猜测,你在从float到int的转换中得到了剪辑。
-
sin
函数能输出的最大值为1.0。 - 将输出乘以2^15,即32768。
- 最大正号短码是32767。
您在不同频率下经历剪切的原因是正弦函数在sin(1+4k*pi/2)
处仅达到1.0,其中k是任意正整数。某些频率(例如441Hz)会经常达到1.0,而其他频率则不会。
解决方法是将浮点数乘以((2^15)-1)
更正后的代码如下,而不是乘以每个float: float * 2^15,我正在做:float * 0x7FFF,正如夹克所指出的,变成:float * ((2^15)-1):
import java.lang.Math;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CreateSine
{
static String fileNameString;
static File file;
static String filePath;
static RandomAccessFile raw;
static int byteCount = 0;
static float freq;
static int sRate = 44100;
static int bitDepth = 16;
static int nChannels = 1;
static int dur;
static float changeRate;
public static void main(String[] args)
{
freq = Float.parseFloat(args[0]);
changeRate = (float)((2.0 * Math.PI * freq) / sRate);
dur = Integer.parseInt(args[1]);
fileNameString = (String)args[2] + ".wav";
file = new File(fileNameString);
filePath = file.getAbsolutePath();
try
{
raw = new RandomAccessFile(filePath, "rw");
raw.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed
raw.writeBytes("RIFF");
raw.writeInt(0); // Final file size not known yet, write 0. This is = sample count + 36 bytes from header.
raw.writeBytes("WAVE");
raw.writeBytes("fmt ");
raw.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM
raw.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
raw.writeShort(Short.reverseBytes((short)nChannels));// Number of channels, 1 for mono, 2 for stereo
raw.writeInt(Integer.reverseBytes(sRate)); // Sample rate
raw.writeInt(Integer.reverseBytes(sRate*bitDepth*nChannels/8)); // Byte rate, SampleRate*NumberOfChannels*bitDepth/8
raw.writeShort(Short.reverseBytes((short)(nChannels*bitDepth/8))); // Block align, NumberOfChannels*bitDepth/8
raw.writeShort(Short.reverseBytes((short)bitDepth)); // Bit Depth
raw.writeBytes("data");
raw.writeInt(0); // Data chunk size not known yet, write 0. This is = sample count.
}
catch(IOException e)
{
System.out.println("I/O exception occured while writing data");
}
for (int i = 0; i < sRate*dur; i++)
{
writeSample( (float)Math.sin( i * changeRate ) );
}
closeFile();
System.out.print("Finished");
}
static void writeSample(float floatValue)
{
try
{
char shortSample = (char)( (floatValue)*0x7FFF );
raw.writeShort(Character.reverseBytes(shortSample));
byteCount += 2;
}
catch(IOException e)
{
System.out.println("I/O exception occured while writing data");
}
}
static void closeFile()
{
try
{
raw.seek(4); // Write size to RIFF header
raw.writeInt(Integer.reverseBytes(byteCount + 36));
raw.seek(40); // Write size to Subchunk2Size field
raw.writeInt(Integer.reverseBytes(byteCount));
raw.close();
}
catch(IOException e)
{
System.out.println("I/O exception occured while closing output file");
}
}
}