Code for a sine sweep

Submitted by mic on Sat, 06/30/2018 - 21:52

I have been testing various sine sweeps for use in impulse reverbs. I thought it would be useful to show the Java code for creating a short wave file with a sine sweep. The code is simple, but also interesting. It is interesting not only because it creates a sine sweep, but also because it shows the structure of simple wave files. Beginner DSP designers, who want to create or read wave files, may find it useful.

This code is not the most elegant, but it should be easily understood.

Random access files

Random access files are not needed for this little piece of code, but random-access implementations for wave files are a good idea. They allow skipping through the RIFF and wave chunks that are not needed. They also allow moving back and forth through the audio data in the wave data chunk, such as when fast forwarding or rewinding.

Wave data

Data in the wave file – audio data and format data – are stored with the least important byte first and most important byte last (little endian). Since there are no guarantees that all operating systems will store numbers with the appropriate byte order, we need a couple of functions to enforce the byte order.

Specifically, we need to be able to write four-byte integers and two-byte short integers. Simplistically, the code can be as follows.

public static void writeInt(RandomAccessFile file, long value) throws IOException
{
   long b0 = value & 0x000000ff;
   long b1 = (value & 0x0000ff00) >> 8;
   long b2 = (value & 0x00ff0000) >> 16;
   long b3 = (value & 0xff000000) >> 24;
   file.writeByte((byte) b0);
   file.writeByte((byte) b1);
   file.writeByte((byte) b2);
   file.writeByte((byte) b3);
}

public static void writeShort(RandomAccessFile file, int value) throws IOException
{
   int b0 = value & 0x000000ff;
   int b1 = (value & 0x0000ff00) >> 8;
   file.writeByte((byte) b0);
   file.writeByte((byte) b1);
}

Sine sweep

Below is the code to create a sine sweep. Here are the steps.

  • We are designing a sine sweep that sweeps the frequencies between 50 Hz and 1000 Hz in 1 second of time and are placing the result in a wave file with the sampling rate 44.1 KHz and the sampling resolution 16 bits. Hence, we have the initial declarations.
  • We begin by writing the RIFF identification "RIFF".
  • We then write the size of the RIFF file. The size of the RIFF file includes: 1) 4 bytes for the RIFF sub-identification "WAVE"; 2) 4 bytes for the identification "fmt "; 3) 4 bytes for the size of the format chunk; 4) 16 bytes for the actual format chunk (the size of the format chunk is 16 bytes, since we do not count the sizes of the identification and the format chunk size); 5) 4 bytes for the identification "data"; and 6) samplingrate * channels * samplingresolution * T / 8 bytes for the audio data.
  • We write the "WAVE" sub-identification to note that this RIFF file is a WAVE file.
  • We write the identification of the format chunk.
  • We write the size of the format chunk (less 4 bytes for the identification and 4 bytes for the size).
  • We write various pieces of the format chunk. We will store PCM data and hence the compression code is 1. The rest are the number of channels, the sampling rate, the average bytes per second, the block align (size of sample for all channels combined), and the number of significant bits per sample (the sampling resolution).
  • We write the identification of the data chunk.
  • We write the audio data.

There are two types of sine sweeps here – a linear one (commented out) and an exponential one.

The value of the sine sweep at each audio sample will initially be between -1 and 1. We scale it up to two thirds of the range allowed by 16-bit recording (this 2/3 choice is random and can be changed). When writing the resulting values, we do not use writeShort, as writeShort is technically designed to write unsigned two-byte integers, whereas the audio data has a sign (they are positive or negative).

You can hear the resulting wave files in the wiki topic Sine sweep.

try
{
   float f0 = 50;
   float f1 = 1000;
   float T = 1;
   float samplingrate = 44100F;
   int samplingresolution = 16;
   int channels = 1;

   // open a wave file
   RandomAccessFile file = new RandomAccessFile("chirp.wav", "rw");

   // write the header
   file.seek(0);
   file.writeBytes("RIFF");
   writeInt(file, (int) (4 + 4 + 4 + 16 + 4 + samplingrate * channels *
       samplingresolution * T / 8));
   file.writeBytes("WAVE");

   // write the format chunk
   file.writeBytes("fmt ");
   writeInt(file, 16L); // size of format chunk
   writeShort(file, 1); // format type
   writeShort(file, channels);
   writeInt(file, (int) samplingrate);
   writeInt(file, ((int) samplingrate * channels * samplingresolution) / 8);
   writeShort(file, (channels * samplingresolution) / 8);
   writeShort(file, samplingresolution);

   // write the data chunk
   file.writeBytes("data");
   writeInt(file, (int) (samplingrate * channels * samplingresolution * T / 8));

   // write the sine sweep data
   for(int i = 0; i    {
       double t = (double) i / samplingrate;
       //double value = Math.sin(2D * Math.PI
           * (f0 * t + (f1 - f0) * t * t / (2 * T)));
       double value = Math.sin(2D * Math.PI * f0 * T
           * (Math.pow(f1 / f0, t / T) - 1) / (Math.log(f1 / f0)));
       int ivalue = (int) Math.min(Math.max(value * Short.MAX_VALUE * 2 / 3,
           Short.MIN_VALUE), Short.MAX_VALUE);
       file.writeByte((byte) (ivalue & 0xff));
       file.writeByte((byte) (ivalue >>> 8 & 0xff));
   }

   file.close();
}
catch (IOException e)
{
   System.out.println("Exception: " + e);
}

authors: mic

Add new comment

Filtered HTML

  • Freelinking helps you easily create HTML links. Links take the form of [[indicator:target|Title]]. By default (no indicator): Click to view a local node.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.