Intro

This is part of a project I have started to see if I can create a full song using just code. I started out writing in C, but for creating more interactive demos I decided to port over to JavaScript. I had never heard of the DataView class in JavaScript. This will ultimately be a lot easier to work with and easier for others to grok as well.

Wav File Format

Head here for a breakdown of the WAV File Format

Demo

Source

const sampleRate = 8000;
const durationSeconds = 10;
const numChannels = 1;
const bytesPerSample = 2 * numChannels;
const bytesPerSecond = sampleRate * bytesPerSample;
const dataLength = bytesPerSecond * durationSeconds;
const headerLength = 44;
const fileLength = dataLength + headerLength;
const bufferData = new Uint8Array(fileLength);
const dataView = new DataView(bufferData.buffer);
const writer = createWriter(dataView);

// HEADER
writer.string("RIFF");
// File Size
writer.uint32(fileLength);
writer.string("WAVE");

writer.string("fmt ");
// Chunk Size
writer.uint32(16);
// Format Tag
writer.uint16(1);
// Number Channels
writer.uint16(numChannels);
// Sample Rate
writer.uint32(sampleRate);
// Bytes Per Second
writer.uint32(bytesPerSecond);
// Bytes Per Sample
writer.uint16(bytesPerSample);
// Bits Per Sample
writer.uint16(bytesPerSample * 8);
writer.string("data");

writer.uint32(dataLength);

for (let i = 0; i < dataLength / 2; i++) {
  const t = i / sampleRate;
  const frequency = 256;
  const volume = 0.6;
  const val = Math.sin(2 * Math.PI * 256 * t) * volume;
  writer.pcm16s(val);
}

const blob = new Blob([dataView.buffer], { type: 'application/octet-stream' });

audioPlayer.src = URL.createObjectURL(blob);

function createWriter(dataView) {
  let pos = 0;

  return {
    string(val) {
      for (let i = 0; i < val.length; i++) {
        dataView.setUint8(pos++, val.charCodeAt(i));
      }
    },
    uint16(val) {
      dataView.setUint16(pos, val, true);
      pos += 2;
    },
    uint32(val) {
      dataView.setUint32(pos, val, true);
      pos += 4;
    },
    pcm16s: function(value) {
      value = Math.round(value * 32768);
      value = Math.max(-32768, Math.min(value, 32767));
      dataView.setInt16(pos, value, true);
      pos += 2;
    },
  }
}