go run main.go
# Result should be in example.wav.
#
# Listen to the result using whatever audio player that supports WAV
Last active
December 7, 2020 21:51
-
-
Save shovon/a7ac2c6f59c7c6c95c96016830b059ca to your computer and use it in GitHub Desktop.
Implementation of writing a WAV file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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