Skip to content

Instantly share code, notes, and snippets.

@Chadtech
Last active August 29, 2015 14:20
Show Gist options
  • Save Chadtech/90f0aad49153c8532bec to your computer and use it in GitHub Desktop.
Save Chadtech/90f0aad49153c8532bec to your computer and use it in GitHub Desktop.
package main
import (
// "bufio"
// "fmt"
// "io"
// "io/ioutil"
"strconv"
"os"
"math"
)
func check(e error){
if e != nil {
panic(e)
}
}
func readWAV( openFileName string ) []int{
readFile, err := os.Open( openFileName )
check(err)
readFile.Seek(40, 0)
var sizeOfAudioBuffer int64 = 0
durationByte := make([]byte, 4)
readFile.Read( durationByte )
sizeOfAudioBuffer += int64(durationByte[0])
sizeOfAudioBuffer += 256 * int64(durationByte[1])
sizeOfAudioBuffer += 65536 * int64(durationByte[2])
sizeOfAudioBuffer += 16777216 * int64(durationByte[3])
durationOfAudio := int64(sizeOfAudioBuffer / 2)
output := make([]int, durationOfAudio )
for datumIndex := int64(0); datumIndex < durationOfAudio; datumIndex++ {
thisSampleByte := make([]byte, 2)
readFile.Read( thisSampleByte )
output[ datumIndex ] = 0
output[ datumIndex ] += int(thisSampleByte[ 0 ])
output[ datumIndex ] += int(thisSampleByte[ 1 ]) * 256
}
return output
}
func writeWAV( audio []int, fileName string) {
savedFile, err := os.Create( fileName)
check(err)
wavHeader := make([]byte, 44)
wavHeader[0] = 82
wavHeader[1] = 73
wavHeader[2] = 70
wavHeader[3] = 70
wavHeader[4] = 36
wavHeader[5] = 8
wavHeader[6] = 0
wavHeader[7] = 0
wavHeader[8] = 87
wavHeader[9] = 65
wavHeader[10] = 86
wavHeader[11] = 69
wavHeader[12] = 102
wavHeader[13] = 109
wavHeader[14] = 116
wavHeader[15] = 32
wavHeader[16] = 16
wavHeader[17] = 0
wavHeader[18] = 0
wavHeader[19] = 0
wavHeader[20] = 1
wavHeader[21] = 0
wavHeader[22] = 1
wavHeader[23] = 0
wavHeader[24] = 68
wavHeader[25] = 172
wavHeader[26] = 0
wavHeader[27] = 0
wavHeader[28] = 68
wavHeader[29] = 172
wavHeader[30] = 0
wavHeader[31] = 0
wavHeader[32] = 4
wavHeader[33] = 0
wavHeader[34] = 16
wavHeader[35] = 0
wavHeader[36] = 100
wavHeader[37] = 97
wavHeader[38] = 116
wavHeader[39] = 97
wavHeader[40] = byte(len(audio) % 256)
wavHeader[41] = byte(len(audio) / 256)
wavHeader[42] = byte(len(audio) / 4096)
wavHeader[43] = byte(len(audio) / 65536)
wavData := make([]byte, (len(audio)) * 2)
for audioIndex := 0; audioIndex < len(audio); audioIndex++ {
wavData[ audioIndex * 2 ] = byte(audio[ audioIndex ] % 256)
wavData[ (audioIndex * 2) + 1 ] = byte(audio[ audioIndex ] / 256)
}
savedFile.Write(wavHeader)
savedFile.Write(wavData)
savedFile.Close()
}
/*
This function will return the numerator and denominator of the rational expression
of the float input. Thus, 1.5 -> [3, 2]
*/
func getFraction ( fraction float64 ) []int {
multiplier := 1
keepLooking := true
for keepLooking {
denominatorCandidate := float64(multiplier) * fraction
distanceToWholeNumber := math.Abs(denominatorCandidate - math.Floor( denominatorCandidate + 0.00005))
if distanceToWholeNumber < 0.00001 {
keepLooking = false
} else {
multiplier++
}
}
numerator := math.Floor((float64(multiplier) * fraction) + 0.5)
denominator := multiplier
output := make([]int, 2)
output[0] = int(numerator)
output[1] = int(denominator)
return output
}
/*
Speeds up an audio buffer by int factor. The technique is to make each sample in the output
an average of factor samples in the input. Thus, if factor is 2, each sample in the output
is the average of samples i and i + 1. (factor 3, i, i + 1, and i + 2)
This will return audio factor times faster, and factor times higher in frequency
*/
func multiplySpeed ( audio []int, factorIncrease int) []int {
output := make([]int, len(audio) / factorIncrease )
for outputIndex := 0; outputIndex < len(output); outputIndex++ {
outputsSample := 0
for factorIndex := 0; factorIndex < factorIncrease; factorIndex++ {
signedAudio := 0
signedAudio += audio[ (outputIndex * factorIncrease) + factorIndex ]
if signedAudio > 32767 {
signedAudio -= 65535
}
outputsSample += signedAudio / factorIncrease
}
output[ outputIndex ] = outputsSample
}
return output
}
/*
Slows down an audio buffer by int factor. The technique is to take the difference between
samples i and i + 1, divide it by factor, and then transform the input audio, into the output
containing factor samples after each one of the input. For example, if the input audio is [5, 8],
and the factor is 3, the output is [5, 6, 7, 8] ( [ 5, 5 + (|5 - 8| / 3), 5 + (2 * (|5 - 8| / 3)), 8])
This will return audio factor times slower, and factor times lower in frequency.
*/
func divideSpeed ( audio []int, factorDecrease int) []int {
output := make([]int, len(audio) * factorDecrease )
for audioIndex := 0; audioIndex < (len(audio) - 1); audioIndex++ {
signedAudio := 0
signedAudio += audio[ audioIndex ]
signedAudioNext := 0
signedAudioNext += audio[ audioIndex + 1]
if signedAudio > 32767 {
signedAudio -= 65535
}
if signedAudioNext > 32767 {
signedAudioNext -= 65535
}
differenceBetweenSamples := signedAudioNext - signedAudio
singleInterval := differenceBetweenSamples / factorDecrease
for factorIndex := 0; factorIndex < factorDecrease; factorIndex++ {
output[ (audioIndex * factorDecrease) + factorIndex ] = signedAudio + (factorIndex * singleInterval)
}
}
return output
}
/*
Change pitch will change the frequency of the input audio, while leaving the length largely unaffected.
The input audio is cut into many very small segments of audio, whereupon the speed of each segment is divided,
then multipied, by the numerator and denominator respectively of getFraction( factor ). Each segment is then
added to the output audio buffer at the same place where it started in the input audio buffer. Thus the overall
profile of the input sound is preserved, though each moment in the sound has been altered in speed and frequency.
*/
func changePitch ( audio []int, factor float64 ) []int {
fraction := getFraction( factor )
grainRate := 1523
grainLength := 4192
numberOfGrains := (len( audio ) / grainRate) - 5
grains := make( [][]int, numberOfGrains )
output := make( []int, len(audio) + int(float64(grainLength) / factor) + 1)
for audioIndex := 0; audioIndex < len(audio); audioIndex++ {
output[ audioIndex ] = 0
}
for grainIndex := 0; grainIndex < (numberOfGrains - 1); grainIndex++ {
grains[ grainIndex ] = make([]int, grainLength)
for sampleIndex := 0; sampleIndex < grainLength; sampleIndex++ {
grains[ grainIndex ][ sampleIndex ] = audio[ (grainIndex * grainRate) + sampleIndex ]
}
}
audioIndexOfLastGrain := len( audio ) - ((numberOfGrains - 1) * grainRate)
grains[ len(grains) - 1 ] = make( []int, len(audio) - audioIndexOfLastGrain )
for sampleIndex := 0 ; sampleIndex < (len( audio ) - audioIndexOfLastGrain); sampleIndex++ {
grains[ len(grains) - 1 ][ sampleIndex ] = audio[ sampleIndex + audioIndexOfLastGrain ]
}
pitchedGrains := make( [][]int, numberOfGrains )
for grainIndex := 0; grainIndex < len(grains); grainIndex++ {
thisGrain := grains[ grainIndex ]
thisGrain = divideSpeed( thisGrain, fraction[1] )
thisGrain = multiplySpeed (thisGrain, fraction[0] )
pitchedGrains[ grainIndex ] = make( []int, len(thisGrain) )
for pitchedGrainIndex := 0; pitchedGrainIndex < len(thisGrain); pitchedGrainIndex++ {
pitchedGrains[ grainIndex ][ pitchedGrainIndex ] = thisGrain[ pitchedGrainIndex ]
}
}
for grainIndex := 0; grainIndex < (len( pitchedGrains ) - 1); grainIndex++ {
for sampleIndex := 0; sampleIndex < len( pitchedGrains[grainIndex] ); sampleIndex++ {
thisSample := 0
thisSample += pitchedGrains[ grainIndex ][ sampleIndex ]
if thisSample > 32767 {
thisSample -= 65535
}
thisSample /= 5
output[ (grainIndex * grainRate) + sampleIndex ] += thisSample
}
}
return output
}
func main() {
fileToOpen := os.Args[1]
saveFileName := os.Args[2]
factor, err := strconv.ParseFloat( os.Args[3], 64 )
check(err)
audio := readWAV( fileToOpen )
audio = changePitch( audio, factor)
writeWAV( audio, saveFileName )
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment