Skip to content

Instantly share code, notes, and snippets.

@shovon
Last active December 7, 2020 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shovon/a7ac2c6f59c7c6c95c96016830b059ca to your computer and use it in GitHub Desktop.
Save shovon/a7ac2c6f59c7c6c95c96016830b059ca to your computer and use it in GitHub Desktop.
Implementation of writing a WAV file.

Usage

go run main.go
# Result should be in example.wav.
#
# Listen to the result using whatever audio player that supports WAV
// My adaptation of the original C++ implementation:
// https://www.cplusplus.com/forum/beginner/166954/
package main
import (
"encoding/binary"
"math"
"os"
)
const sampleRate = 44100
const twoPi float64 = 6.283185307179586476925286766559
const maxUint16 = ^uint16(0)
const maxInt16 = int16(maxUint16 >> 1)
const seconds float64 = 2.5
const frequency float64 = 261.626 // middle C
var uint16Buf []byte = make([]byte, 2)
var uint32Buf []byte = make([]byte, 4)
func writeUint32(f *os.File, v uint32) {
binary.LittleEndian.PutUint32(uint32Buf, v)
f.Write(uint32Buf)
}
func writeUint16(f *os.File, v uint16) {
binary.LittleEndian.PutUint16(uint16Buf, v)
f.Write(uint16Buf)
}
func main() {
// Open a file. If not exist, create it. We should be able to overwrite it.
f, err := os.OpenFile("example.wav", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
panic(err)
}
// T H E H E A D E R
f.Write([]byte("RIFF----WAVEfmt ")) // (chunk size to be filled in later)
writeUint32(f, 16) // no extension data*
writeUint16(f, 1) // PCM - integer samples
writeUint16(f, 2) // two channels (stereo file)
writeUint32(f, sampleRate) // samples per second
writeUint32(f, sampleRate*4*2/8) // Not sure what this is
writeUint16(f, 4) // data block size (two 16-bit integers)
writeUint16(f, 16) // number of bits per sample*
// *not sure what is meant by "no extension data"
// *use a multiple of 8
datachunkPos, err := f.Seek(0, os.SEEK_CUR)
if err != nil {
panic(err)
}
f.Write([]byte("data----")) // The length of the data (filled in later)
// T H E D A T A
hz := float64(sampleRate)
n := int(hz * seconds)
for i := 0; i < n; i++ {
amplitude := float64(i) / float64(n) * float64(maxInt16)
value := math.Sin((twoPi * float64(i) * frequency) / hz)
writeUint16(f, uint16(amplitude*value))
writeUint16(f, uint16((float64(maxInt16)-amplitude)*value))
}
fileLength, err := f.Seek(0, os.SEEK_CUR)
// Seek backwards to fill in missing data.
f.Seek(datachunkPos+4, os.SEEK_SET)
writeUint32(f, uint32(fileLength-datachunkPos+8))
f.Seek(0+4, os.SEEK_SET)
writeUint32(f, uint32(fileLength-8))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment