2016-10-24 2 views
1

Следующий код должен создать файл, содержащий синусоидальную волну. На типичных частотах (220 Гц, 440 Гц, 880 Гц) он идет отлично, но на многих других частотах он, например, не принимает 225 Гц, 883 Гц и так далее. Что делать, чтобы получить хорошую синусоидальную волну для любой частоты?Как сгенерировать WAV-файл с синусоидальной волной и определяемой пользователем продолжительностью и частотой?

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"); 
     } 
    } 
} 

Благодарим за помощь.

ответ

0

Ваш вопрос на самом деле не описывает, что проблема, кроме как сказать, что это не приятно. Я собираюсь рискнуть предположить, что вы получаете обрезание в вашем преобразовании от float до int.

  • Максимальное значение, которое может выдавать функция sin, равно 1,0.
  • умножить объем производства на 2^15, или 32768.
  • Наибольшими положительным подписали коротким является 32767.

Причины вы испытываете отсечения на разных частотах, что функция синуса попадет только 1,0 в sin(1+4k*pi/2), где k - любое положительное целое число. Некоторые частоты (например, 441 Гц) будут очень часто поражать 1.0, а другие - нет.

Решение состоит в умножении чисел с плавающей запятой на ((2^15)-1)

0

Исправленный код приведен ниже, вместо того, чтобы умножать каждый поплавок: float * 2^15, я делаю: float * 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"); 
     } 
    } 
}