如何生成具有正弦波和用户定义的持续时间和频率的.wav文件?

时间:2016-10-24 00:51:37

标签: java audio signal-processing

以下代码应创建包含正弦波的文件。在典型的频率(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");
        }
    }
}

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

更正后的代码如下,而不是乘以每个浮点数:float * 2 ^ 15,我正在做:浮动* 0x7FFF,如jaket所示,转为: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");
        }
    }
}

答案 1 :(得分:1)

你的问题并没有真正描述问题所在,而是说它不好。我将冒险猜测你在从float转换为int时会出现削波。

  • sin函数可输出的最大值为1.0。
  • 您将输出乘以2 ^ 15或32768。
  • 最大的正面签名空头是32767。

您在不同频率下经历削波的原因是正弦函数仅在sin(1+4k*pi/2)处达到1.0,其中k是任何正整数。某些频率(例如441Hz)将经常达到1.0而其他频率则不会。

解决方案是将浮点数乘以((2^15)-1)